mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Improved error spans for call errors:
1. When calling a non-callable expression the error span is on the call target not on the whole call 2. When calling a method, the error for overload resolution now includes the arguments (this was previously regressed by #31414)
This commit is contained in:
@@ -21298,8 +21298,34 @@ namespace ts {
|
||||
return Debug.fail();
|
||||
}
|
||||
}
|
||||
function getDiagnosticSpanForCallNode(node: CallExpression, doNotIncludeArguments?: boolean) {
|
||||
let start: number;
|
||||
let length: number;
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
|
||||
function getArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
|
||||
if (isPropertyAccessExpression(node.expression)) {
|
||||
const nameSpan = getErrorSpanForNode(sourceFile, node.expression.name);
|
||||
start = nameSpan.start;
|
||||
length = doNotIncludeArguments ? nameSpan.length : node.end - start;
|
||||
}
|
||||
else {
|
||||
const expressionSpan = getErrorSpanForNode(sourceFile, node.expression);
|
||||
start = expressionSpan.start;
|
||||
length = doNotIncludeArguments ? expressionSpan.length : node.end - start;
|
||||
}
|
||||
return { start, length, sourceFile };
|
||||
}
|
||||
function getDiagnosticForCallNode(node: CallLikeExpression, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
|
||||
if (isCallExpression(node)) {
|
||||
const { sourceFile, start, length } = getDiagnosticSpanForCallNode(node);
|
||||
return createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
else {
|
||||
return createDiagnosticForNode(node, message, arg0, arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
|
||||
function getArgumentArityError(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
let max = Number.NEGATIVE_INFINITY;
|
||||
let belowArgCount = Number.NEGATIVE_INFINITY;
|
||||
@@ -21346,11 +21372,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (min < argCount && argCount < max) {
|
||||
return createDiagnosticForNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
|
||||
return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
|
||||
}
|
||||
|
||||
if (!hasSpreadArgument && argCount < min) {
|
||||
const diagnostic = createDiagnosticForNode(node, error, paramRange, argCount);
|
||||
const diagnostic = getDiagnosticForCallNode(node, error, paramRange, argCount);
|
||||
return related ? addRelatedInfo(diagnostic, related) : diagnostic;
|
||||
}
|
||||
|
||||
@@ -21425,8 +21451,7 @@ namespace ts {
|
||||
reorderCandidates(signatures, candidates);
|
||||
if (!candidates.length) {
|
||||
if (reportErrors) {
|
||||
const errorNode = getCallErrorNode(node);
|
||||
diagnostics.add(createDiagnosticForNode(errorNode, Diagnostics.Call_target_does_not_contain_any_signatures));
|
||||
diagnostics.add(getDiagnosticForCallNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
@@ -21504,13 +21529,12 @@ namespace ts {
|
||||
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
|
||||
// skip the checkApplicableSignature check.
|
||||
if (reportErrors) {
|
||||
const errorNode = getCallErrorNode(node);
|
||||
|
||||
if (candidateForArgumentError) {
|
||||
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, CheckMode.Normal, /*reportErrors*/ true);
|
||||
}
|
||||
else if (candidateForArgumentArityError) {
|
||||
diagnostics.add(getArgumentArityError(errorNode, [candidateForArgumentArityError], args));
|
||||
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args));
|
||||
}
|
||||
else if (candidateForTypeArgumentError) {
|
||||
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, fallbackError);
|
||||
@@ -21518,31 +21542,19 @@ namespace ts {
|
||||
else {
|
||||
const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments));
|
||||
if (signaturesWithCorrectTypeArgumentArity.length === 0) {
|
||||
diagnostics.add(getTypeArgumentArityError(errorNode, signatures, typeArguments!));
|
||||
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!));
|
||||
}
|
||||
else if (!isDecorator) {
|
||||
diagnostics.add(getArgumentArityError(errorNode, signaturesWithCorrectTypeArgumentArity, args));
|
||||
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args));
|
||||
}
|
||||
else if (fallbackError) {
|
||||
diagnostics.add(createDiagnosticForNode(errorNode, fallbackError));
|
||||
diagnostics.add(getDiagnosticForCallNode(node, fallbackError));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray);
|
||||
|
||||
function getCallErrorNode(node: CallLikeExpression): Node {
|
||||
if (isCallExpression(node)) {
|
||||
if (isPropertyAccessExpression(node.expression)) {
|
||||
return node.expression.name;
|
||||
}
|
||||
else {
|
||||
return node.expression;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
|
||||
candidateForArgumentError = undefined;
|
||||
candidateForArgumentArityError = undefined;
|
||||
@@ -21825,7 +21837,7 @@ namespace ts {
|
||||
relatedInformation = createDiagnosticForNode(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon);
|
||||
}
|
||||
}
|
||||
invocationError(node, apparentType, SignatureKind.Call, relatedInformation);
|
||||
invocationError(node.expression, apparentType, SignatureKind.Call, relatedInformation);
|
||||
}
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
@@ -21942,7 +21954,7 @@ namespace ts {
|
||||
return signature;
|
||||
}
|
||||
|
||||
invocationError(node, expressionType, SignatureKind.Construct);
|
||||
invocationError(node.expression, expressionType, SignatureKind.Construct);
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
@@ -22089,8 +22101,13 @@ namespace ts {
|
||||
Diagnostics.This_expression_is_not_constructable
|
||||
);
|
||||
}
|
||||
function invocationError(node: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) {
|
||||
const diagnostic = createDiagnosticForNodeFromMessageChain(node, invocationErrorDetails(apparentType, kind));
|
||||
function invocationError(errorTarget: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) {
|
||||
const diagnostic = createDiagnosticForNodeFromMessageChain(errorTarget, invocationErrorDetails(apparentType, kind));
|
||||
if (isCallExpression(errorTarget.parent)) {
|
||||
const { start, length } = getDiagnosticSpanForCallNode(errorTarget.parent, /* doNotIncludeArguments */ true);
|
||||
diagnostic.start = start;
|
||||
diagnostic.length = length;
|
||||
}
|
||||
diagnostics.add(diagnostic);
|
||||
invocationErrorRecovery(apparentType, kind, relatedInformation ? addRelatedInfo(diagnostic, relatedInformation) : diagnostic);
|
||||
}
|
||||
@@ -22129,7 +22146,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (!callSignatures.length) {
|
||||
invocationError(node, apparentType, SignatureKind.Call);
|
||||
invocationError(node.tag, apparentType, SignatureKind.Call);
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
@@ -22187,7 +22204,7 @@ namespace ts {
|
||||
if (!callSignatures.length) {
|
||||
let errorInfo = invocationErrorDetails(apparentType, SignatureKind.Call);
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, headMessage);
|
||||
const diag = createDiagnosticForNodeFromMessageChain(node, errorInfo);
|
||||
const diag = createDiagnosticForNodeFromMessageChain(node.expression, errorInfo);
|
||||
diagnostics.add(diag);
|
||||
invocationErrorRecovery(apparentType, SignatureKind.Call, diag);
|
||||
return resolveErrorCall(node);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace ts.codefix {
|
||||
function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const targetKind = Diagnostics.This_expression_is_not_callable.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression;
|
||||
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind && a.getStart() === context.span.start && a.getEnd() === (context.span.start + context.span.length)) as CallExpression | NewExpression;
|
||||
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind) as CallExpression | NewExpression;
|
||||
if (!node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user