Skip to content

Commit 52897db

Browse files
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 microsoft#31414)
1 parent bc07eec commit 52897db

File tree

86 files changed

+306
-215
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+306
-215
lines changed

src/compiler/checker.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21298,8 +21298,34 @@ namespace ts {
2129821298
return Debug.fail();
2129921299
}
2130021300
}
21301+
function getDiagnosticSpanForCallNode(node: CallExpression, doNotIncludeArguments?: boolean) {
21302+
let start: number;
21303+
let length: number;
21304+
const sourceFile = getSourceFileOfNode(node);
2130121305

21302-
function getArgumentArityError(node: Node, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
21306+
if (isPropertyAccessExpression(node.expression)) {
21307+
const nameSpan = getErrorSpanForNode(sourceFile, node.expression.name);
21308+
start = nameSpan.start;
21309+
length = doNotIncludeArguments ? nameSpan.length : node.end - start;
21310+
}
21311+
else {
21312+
const expressionSpan = getErrorSpanForNode(sourceFile, node.expression);
21313+
start = expressionSpan.start;
21314+
length = doNotIncludeArguments ? expressionSpan.length : node.end - start;
21315+
}
21316+
return { start, length, sourceFile };
21317+
}
21318+
function getDiagnosticForCallNode(node: CallLikeExpression, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
21319+
if (isCallExpression(node)) {
21320+
const { sourceFile, start, length } = getDiagnosticSpanForCallNode(node);
21321+
return createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2, arg3);
21322+
}
21323+
else {
21324+
return createDiagnosticForNode(node, message, arg0, arg1, arg2, arg3);
21325+
}
21326+
}
21327+
21328+
function getArgumentArityError(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, args: ReadonlyArray<Expression>) {
2130321329
let min = Number.POSITIVE_INFINITY;
2130421330
let max = Number.NEGATIVE_INFINITY;
2130521331
let belowArgCount = Number.NEGATIVE_INFINITY;
@@ -21346,11 +21372,11 @@ namespace ts {
2134621372
}
2134721373
}
2134821374
if (min < argCount && argCount < max) {
21349-
return createDiagnosticForNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
21375+
return getDiagnosticForCallNode(node, Diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, argCount, belowArgCount, aboveArgCount);
2135021376
}
2135121377

2135221378
if (!hasSpreadArgument && argCount < min) {
21353-
const diagnostic = createDiagnosticForNode(node, error, paramRange, argCount);
21379+
const diagnostic = getDiagnosticForCallNode(node, error, paramRange, argCount);
2135421380
return related ? addRelatedInfo(diagnostic, related) : diagnostic;
2135521381
}
2135621382

@@ -21425,8 +21451,7 @@ namespace ts {
2142521451
reorderCandidates(signatures, candidates);
2142621452
if (!candidates.length) {
2142721453
if (reportErrors) {
21428-
const errorNode = getCallErrorNode(node);
21429-
diagnostics.add(createDiagnosticForNode(errorNode, Diagnostics.Call_target_does_not_contain_any_signatures));
21454+
diagnostics.add(getDiagnosticForCallNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
2143021455
}
2143121456
return resolveErrorCall(node);
2143221457
}
@@ -21504,45 +21529,32 @@ namespace ts {
2150421529
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
2150521530
// skip the checkApplicableSignature check.
2150621531
if (reportErrors) {
21507-
const errorNode = getCallErrorNode(node);
2150821532

2150921533
if (candidateForArgumentError) {
2151021534
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, CheckMode.Normal, /*reportErrors*/ true);
2151121535
}
2151221536
else if (candidateForArgumentArityError) {
21513-
diagnostics.add(getArgumentArityError(errorNode, [candidateForArgumentArityError], args));
21537+
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args));
2151421538
}
2151521539
else if (candidateForTypeArgumentError) {
2151621540
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, fallbackError);
2151721541
}
2151821542
else {
2151921543
const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments));
2152021544
if (signaturesWithCorrectTypeArgumentArity.length === 0) {
21521-
diagnostics.add(getTypeArgumentArityError(errorNode, signatures, typeArguments!));
21545+
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!));
2152221546
}
2152321547
else if (!isDecorator) {
21524-
diagnostics.add(getArgumentArityError(errorNode, signaturesWithCorrectTypeArgumentArity, args));
21548+
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args));
2152521549
}
2152621550
else if (fallbackError) {
21527-
diagnostics.add(createDiagnosticForNode(errorNode, fallbackError));
21551+
diagnostics.add(getDiagnosticForCallNode(node, fallbackError));
2152821552
}
2152921553
}
2153021554
}
2153121555

2153221556
return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray);
2153321557

21534-
function getCallErrorNode(node: CallLikeExpression): Node {
21535-
if (isCallExpression(node)) {
21536-
if (isPropertyAccessExpression(node.expression)) {
21537-
return node.expression.name;
21538-
}
21539-
else {
21540-
return node.expression;
21541-
}
21542-
}
21543-
return node;
21544-
}
21545-
2154621558
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
2154721559
candidateForArgumentError = undefined;
2154821560
candidateForArgumentArityError = undefined;
@@ -21825,7 +21837,7 @@ namespace ts {
2182521837
relatedInformation = createDiagnosticForNode(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon);
2182621838
}
2182721839
}
21828-
invocationError(node, apparentType, SignatureKind.Call, relatedInformation);
21840+
invocationError(node.expression, apparentType, SignatureKind.Call, relatedInformation);
2182921841
}
2183021842
return resolveErrorCall(node);
2183121843
}
@@ -21942,7 +21954,7 @@ namespace ts {
2194221954
return signature;
2194321955
}
2194421956

21945-
invocationError(node, expressionType, SignatureKind.Construct);
21957+
invocationError(node.expression, expressionType, SignatureKind.Construct);
2194621958
return resolveErrorCall(node);
2194721959
}
2194821960

@@ -22089,8 +22101,13 @@ namespace ts {
2208922101
Diagnostics.This_expression_is_not_constructable
2209022102
);
2209122103
}
22092-
function invocationError(node: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) {
22093-
const diagnostic = createDiagnosticForNodeFromMessageChain(node, invocationErrorDetails(apparentType, kind));
22104+
function invocationError(errorTarget: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) {
22105+
const diagnostic = createDiagnosticForNodeFromMessageChain(errorTarget, invocationErrorDetails(apparentType, kind));
22106+
if (isCallExpression(errorTarget.parent)) {
22107+
const { start, length } = getDiagnosticSpanForCallNode(errorTarget.parent, /* doNotIncludeArguments */ true);
22108+
diagnostic.start = start;
22109+
diagnostic.length = length;
22110+
}
2209422111
diagnostics.add(diagnostic);
2209522112
invocationErrorRecovery(apparentType, kind, relatedInformation ? addRelatedInfo(diagnostic, relatedInformation) : diagnostic);
2209622113
}
@@ -22129,7 +22146,7 @@ namespace ts {
2212922146
}
2213022147

2213122148
if (!callSignatures.length) {
22132-
invocationError(node, apparentType, SignatureKind.Call);
22149+
invocationError(node.tag, apparentType, SignatureKind.Call);
2213322150
return resolveErrorCall(node);
2213422151
}
2213522152

@@ -22187,7 +22204,7 @@ namespace ts {
2218722204
if (!callSignatures.length) {
2218822205
let errorInfo = invocationErrorDetails(apparentType, SignatureKind.Call);
2218922206
errorInfo = chainDiagnosticMessages(errorInfo, headMessage);
22190-
const diag = createDiagnosticForNodeFromMessageChain(node, errorInfo);
22207+
const diag = createDiagnosticForNodeFromMessageChain(node.expression, errorInfo);
2219122208
diagnostics.add(diag);
2219222209
invocationErrorRecovery(apparentType, SignatureKind.Call, diag);
2219322210
return resolveErrorCall(node);

src/services/codefixes/fixInvalidImportSyntax.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ namespace ts.codefix {
4040
function getActionsForUsageOfInvalidImport(context: CodeFixContext): CodeFixAction[] | undefined {
4141
const sourceFile = context.sourceFile;
4242
const targetKind = Diagnostics.This_expression_is_not_callable.code === context.errorCode ? SyntaxKind.CallExpression : SyntaxKind.NewExpression;
43-
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;
43+
const node = findAncestor(getTokenAtPosition(sourceFile, context.span.start), a => a.kind === targetKind) as CallExpression | NewExpression;
4444
if (!node) {
4545
return [];
4646
}

tests/baselines/reference/arityErrorRelatedSpanBindingPattern.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ tests/cases/compiler/arityErrorRelatedSpanBindingPattern.ts(7,1): error TS2554:
88
function bar(a, b, [c]): void {}
99

1010
foo("", 0);
11-
~~~
11+
~~~~~~~~~~
1212
!!! error TS2554: Expected 3 arguments, but got 2.
1313
!!! related TS6211 tests/cases/compiler/arityErrorRelatedSpanBindingPattern.ts:1:20: An argument matching this binding pattern was not provided.
1414

1515
bar("", 0);
16-
~~~
16+
~~~~~~~~~~
1717
!!! error TS2554: Expected 3 arguments, but got 2.
1818
!!! related TS6211 tests/cases/compiler/arityErrorRelatedSpanBindingPattern.ts:3:20: An argument matching this binding pattern was not provided.
1919

tests/baselines/reference/baseCheck.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ tests/cases/compiler/baseCheck.ts(26,9): error TS2304: Cannot find name 'x'.
3030
}
3131

3232
class D extends C { constructor(public z: number) { super(this.z) } } // too few params
33-
~~~~~
33+
~~~~~~~~~~~~~
3434
!!! error TS2554: Expected 2 arguments, but got 1.
3535
!!! related TS6210 tests/cases/compiler/baseCheck.ts:1:34: An argument for 'y' was not provided.
3636
~~~~

tests/baselines/reference/betterErrorForAccidentalCall.errors.txt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,33 @@ tests/cases/compiler/betterErrorForAccidentalCall.ts(13,1): error TS2349: This e
1414
declare function foo(): string;
1515

1616
foo()(1 as number).toString();
17-
~~~~~~~~~~~~~~~~~~
17+
~~~~~
1818
!!! error TS2349: This expression is not callable.
1919
!!! error TS2349: Type 'String' has no call signatures.
2020

2121
foo() (1 as number).toString();
22-
~~~~~~~~~~~~~~~~~~~~~
22+
~~~~~
2323
!!! error TS2349: This expression is not callable.
2424
!!! error TS2349: Type 'String' has no call signatures.
2525

2626
foo()
2727
~~~~~
28-
(1 as number).toString();
29-
~~~~~~~~~~~~~
3028
!!! error TS2349: This expression is not callable.
3129
!!! error TS2349: Type 'String' has no call signatures.
3230
!!! related TS2734 tests/cases/compiler/betterErrorForAccidentalCall.ts:7:1: It is highly likely that you are missing a semicolon.
31+
(1 as number).toString();
3332

3433
foo()
3534
~~~~~
36-
(1 + 2).toString();
37-
~~~~~~~~~~~
3835
!!! error TS2349: This expression is not callable.
3936
!!! error TS2349: Type 'String' has no call signatures.
4037
!!! related TS2734 tests/cases/compiler/betterErrorForAccidentalCall.ts:10:1: It is highly likely that you are missing a semicolon.
38+
(1 + 2).toString();
4139

4240
foo()
4341
~~~~~
44-
(<number>1).toString();
45-
~~~~~~~~~~~~~~~
4642
!!! error TS2349: This expression is not callable.
4743
!!! error TS2349: Type 'String' has no call signatures.
4844
!!! related TS2734 tests/cases/compiler/betterErrorForAccidentalCall.ts:13:1: It is highly likely that you are missing a semicolon.
45+
(<number>1).toString();
4946

tests/baselines/reference/betterErrorForUnionCall.errors.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ tests/cases/compiler/betterErrorForUnionCall.ts(8,1): error TS2349: This express
1010
==== tests/cases/compiler/betterErrorForUnionCall.ts (3 errors) ====
1111
declare const union: { a: string } | { b: string }
1212
union("");
13-
~~~~~~~~~
13+
~~~~~
1414
!!! error TS2349: This expression is not callable.
1515
!!! error TS2349: No constituent of type '{ a: string; } | { b: string; }' is callable.
1616

1717
declare const fnUnion: { a: string } | ((a: string) => void)
1818
fnUnion("");
19-
~~~~~~~~~~~
19+
~~~~~~~
2020
!!! error TS2349: This expression is not callable.
2121
!!! error TS2349: Not all constituents of type '{ a: string; } | ((a: string) => void)' are callable.
2222
!!! error TS2349: Type '{ a: string; }' has no call signatures.
2323

2424
declare const fnUnion2: (<T extends number>(a: T) => void) | (<T>(a: string) => void)
2525
fnUnion2("");
26-
~~~~~~~~~~~~
26+
~~~~~~~~
2727
!!! error TS2349: This expression is not callable.
2828
!!! error TS2349: Each member of the union type '(<T extends number>(a: T) => void) | (<T>(a: string) => void)' has signatures, but none of those signatures are compatible with each other.
2929

tests/baselines/reference/blockScopedSameNameFunctionDeclarationES5.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ tests/cases/compiler/blockScopedSameNameFunctionDeclarationES5.ts(16,1): error T
3333
}
3434
foo(10);
3535
foo(); // not ok - needs number
36-
~~~
36+
~~~~~
3737
!!! error TS2554: Expected 1 arguments, but got 0.
3838
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationES5.ts:1:14: An argument for 'a' was not provided.

tests/baselines/reference/blockScopedSameNameFunctionDeclarationES6.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ tests/cases/compiler/blockScopedSameNameFunctionDeclarationES6.ts(16,1): error T
3333
}
3434
foo(10);
3535
foo(); // not ok - needs number
36-
~~~
36+
~~~~~
3737
!!! error TS2554: Expected 1 arguments, but got 0.
3838
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationES6.ts:1:14: An argument for 'a' was not provided.

tests/baselines/reference/blockScopedSameNameFunctionDeclarationStrictES5.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES5.ts(17,1): e
2929
}
3030
foo(10);
3131
foo(); // not ok - needs number
32-
~~~
32+
~~~~~
3333
!!! error TS2554: Expected 1 arguments, but got 0.
3434
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES5.ts:2:14: An argument for 'a' was not provided.
3535
}
3636
foo(10);
3737
foo(); // not ok - needs number
38-
~~~
38+
~~~~~
3939
!!! error TS2554: Expected 1 arguments, but got 0.
4040
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES5.ts:2:14: An argument for 'a' was not provided.

tests/baselines/reference/blockScopedSameNameFunctionDeclarationStrictES6.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES6.ts(17,1): e
2323
}
2424
foo(10);
2525
foo(); // not ok
26-
~~~
26+
~~~~~
2727
!!! error TS2554: Expected 1 arguments, but got 0.
2828
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES6.ts:2:14: An argument for 'a' was not provided.
2929
}
3030
foo(10);
3131
foo(); // not ok - needs number
32-
~~~
32+
~~~~~
3333
!!! error TS2554: Expected 1 arguments, but got 0.
3434
!!! related TS6210 tests/cases/compiler/blockScopedSameNameFunctionDeclarationStrictES6.ts:2:14: An argument for 'a' was not provided.

tests/baselines/reference/callOnInstance.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ tests/cases/compiler/callOnInstance.ts(10,1): error TS2349: This expression is n
2222

2323
declare class C { constructor(value: number); }
2424
(new C(1))(); // Error for calling an instance
25-
~~~~~~~~~~~~
25+
~~~~~~~~~~
2626
!!! error TS2349: This expression is not callable.
2727
!!! error TS2349: Type 'C' has no call signatures.

tests/baselines/reference/callOverload.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ tests/cases/conformance/expressions/functionCalls/callOverload.ts(11,10): error
1919
!!! error TS2554: Expected 2 arguments, but got 4.
2020
withRest('a', ...n); // no error
2121
withRest();
22-
~~~~~~~~
22+
~~~~~~~~~~
2323
!!! error TS2555: Expected at least 1 arguments, but got 0.
2424
!!! related TS6210 tests/cases/conformance/expressions/functionCalls/callOverload.ts:3:27: An argument for 'a' was not provided.
2525
withRest(...n);

0 commit comments

Comments
 (0)