Skip to content

Commit e9b48e7

Browse files
Improve error spans on chained method calls
1 parent d484163 commit e9b48e7

File tree

6 files changed

+116
-5
lines changed

6 files changed

+116
-5
lines changed

src/compiler/checker.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21223,7 +21223,8 @@ namespace ts {
2122321223
reorderCandidates(signatures, candidates);
2122421224
if (!candidates.length) {
2122521225
if (reportErrors) {
21226-
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
21226+
const errorNode = getCallErrorNode(node);
21227+
diagnostics.add(createDiagnosticForNode(errorNode, Diagnostics.Call_target_does_not_contain_any_signatures));
2122721228
}
2122821229
return resolveErrorCall(node);
2122921230
}
@@ -21301,31 +21302,40 @@ namespace ts {
2130121302
// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
2130221303
// skip the checkApplicableSignature check.
2130321304
if (reportErrors) {
21305+
const errorNode = getCallErrorNode(node);
21306+
2130421307
if (candidateForArgumentError) {
2130521308
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, CheckMode.Normal, /*reportErrors*/ true);
2130621309
}
2130721310
else if (candidateForArgumentArityError) {
21308-
diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args));
21311+
diagnostics.add(getArgumentArityError(errorNode, [candidateForArgumentArityError], args));
2130921312
}
2131021313
else if (candidateForTypeArgumentError) {
2131121314
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, fallbackError);
2131221315
}
2131321316
else {
2131421317
const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments));
2131521318
if (signaturesWithCorrectTypeArgumentArity.length === 0) {
21316-
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!));
21319+
diagnostics.add(getTypeArgumentArityError(errorNode, signatures, typeArguments!));
2131721320
}
2131821321
else if (!isDecorator) {
21319-
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args));
21322+
diagnostics.add(getArgumentArityError(errorNode, signaturesWithCorrectTypeArgumentArity, args));
2132021323
}
2132121324
else if (fallbackError) {
21322-
diagnostics.add(createDiagnosticForNode(node, fallbackError));
21325+
diagnostics.add(createDiagnosticForNode(errorNode, fallbackError));
2132321326
}
2132421327
}
2132521328
}
2132621329

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

21332+
function getCallErrorNode(node: CallLikeExpression): Node {
21333+
if (isCallExpression(node) && isPropertyAccessExpression(node.expression)) {
21334+
return node.expression.name;
21335+
}
21336+
return node;
21337+
}
21338+
2132921339
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
2133021340
candidateForArgumentError = undefined;
2133121341
candidateForArgumentArityError = undefined;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/methodChainError.ts(9,6): error TS2554: Expected 1 arguments, but got 0.
2+
3+
4+
==== tests/cases/compiler/methodChainError.ts (1 errors) ====
5+
class Builder {
6+
method(param: string): Builder {
7+
return this;
8+
}
9+
}
10+
11+
new Builder()
12+
.method("a")
13+
.method();
14+
~~~~~~
15+
!!! error TS2554: Expected 1 arguments, but got 0.
16+
!!! related TS6210 tests/cases/compiler/methodChainError.ts:2:12: An argument for 'param' was not provided.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [methodChainError.ts]
2+
class Builder {
3+
method(param: string): Builder {
4+
return this;
5+
}
6+
}
7+
8+
new Builder()
9+
.method("a")
10+
.method();
11+
12+
//// [methodChainError.js]
13+
var Builder = /** @class */ (function () {
14+
function Builder() {
15+
}
16+
Builder.prototype.method = function (param) {
17+
return this;
18+
};
19+
return Builder;
20+
}());
21+
new Builder()
22+
.method("a")
23+
.method();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/methodChainError.ts ===
2+
class Builder {
3+
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
4+
5+
method(param: string): Builder {
6+
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
7+
>param : Symbol(param, Decl(methodChainError.ts, 1, 11))
8+
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
9+
10+
return this;
11+
>this : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
12+
}
13+
}
14+
15+
new Builder()
16+
>new Builder() .method("a") .method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
17+
>new Builder() .method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
18+
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
19+
20+
.method("a")
21+
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
22+
23+
.method();
24+
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
25+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/compiler/methodChainError.ts ===
2+
class Builder {
3+
>Builder : Builder
4+
5+
method(param: string): Builder {
6+
>method : (param: string) => Builder
7+
>param : string
8+
9+
return this;
10+
>this : this
11+
}
12+
}
13+
14+
new Builder()
15+
>new Builder() .method("a") .method() : Builder
16+
>new Builder() .method("a") .method : (param: string) => Builder
17+
>new Builder() .method("a") : Builder
18+
>new Builder() .method : (param: string) => Builder
19+
>new Builder() : Builder
20+
>Builder : typeof Builder
21+
22+
.method("a")
23+
>method : (param: string) => Builder
24+
>"a" : "a"
25+
26+
.method();
27+
>method : (param: string) => Builder
28+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Builder {
2+
method(param: string): Builder {
3+
return this;
4+
}
5+
}
6+
7+
new Builder()
8+
.method("a")
9+
.method();

0 commit comments

Comments
 (0)