Improve error spans on chained method calls

This commit is contained in:
Dhruv Rajvanshi
2019-05-16 01:37:24 +05:30
parent d484163d0f
commit e9b48e78c7
6 changed files with 116 additions and 5 deletions
+15 -5
View File
@@ -21223,7 +21223,8 @@ namespace ts {
reorderCandidates(signatures, candidates);
if (!candidates.length) {
if (reportErrors) {
diagnostics.add(createDiagnosticForNode(node, Diagnostics.Call_target_does_not_contain_any_signatures));
const errorNode = getCallErrorNode(node);
diagnostics.add(createDiagnosticForNode(errorNode, Diagnostics.Call_target_does_not_contain_any_signatures));
}
return resolveErrorCall(node);
}
@@ -21301,11 +21302,13 @@ 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(node, [candidateForArgumentArityError], args));
diagnostics.add(getArgumentArityError(errorNode, [candidateForArgumentArityError], args));
}
else if (candidateForTypeArgumentError) {
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression | TaggedTemplateExpression | JsxOpeningLikeElement).typeArguments!, /*reportErrors*/ true, fallbackError);
@@ -21313,19 +21316,26 @@ namespace ts {
else {
const signaturesWithCorrectTypeArgumentArity = filter(signatures, s => hasCorrectTypeArgumentArity(s, typeArguments));
if (signaturesWithCorrectTypeArgumentArity.length === 0) {
diagnostics.add(getTypeArgumentArityError(node, signatures, typeArguments!));
diagnostics.add(getTypeArgumentArityError(errorNode, signatures, typeArguments!));
}
else if (!isDecorator) {
diagnostics.add(getArgumentArityError(node, signaturesWithCorrectTypeArgumentArity, args));
diagnostics.add(getArgumentArityError(errorNode, signaturesWithCorrectTypeArgumentArity, args));
}
else if (fallbackError) {
diagnostics.add(createDiagnosticForNode(node, fallbackError));
diagnostics.add(createDiagnosticForNode(errorNode, fallbackError));
}
}
}
return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray);
function getCallErrorNode(node: CallLikeExpression): Node {
if (isCallExpression(node) && isPropertyAccessExpression(node.expression)) {
return node.expression.name;
}
return node;
}
function chooseOverload(candidates: Signature[], relation: Map<RelationComparisonResult>, signatureHelpTrailingComma = false) {
candidateForArgumentError = undefined;
candidateForArgumentArityError = undefined;
@@ -0,0 +1,16 @@
tests/cases/compiler/methodChainError.ts(9,6): error TS2554: Expected 1 arguments, but got 0.
==== tests/cases/compiler/methodChainError.ts (1 errors) ====
class Builder {
method(param: string): Builder {
return this;
}
}
new Builder()
.method("a")
.method();
~~~~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/compiler/methodChainError.ts:2:12: An argument for 'param' was not provided.
@@ -0,0 +1,23 @@
//// [methodChainError.ts]
class Builder {
method(param: string): Builder {
return this;
}
}
new Builder()
.method("a")
.method();
//// [methodChainError.js]
var Builder = /** @class */ (function () {
function Builder() {
}
Builder.prototype.method = function (param) {
return this;
};
return Builder;
}());
new Builder()
.method("a")
.method();
@@ -0,0 +1,25 @@
=== tests/cases/compiler/methodChainError.ts ===
class Builder {
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
method(param: string): Builder {
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
>param : Symbol(param, Decl(methodChainError.ts, 1, 11))
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
return this;
>this : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
}
}
new Builder()
>new Builder() .method("a") .method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
>new Builder() .method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
>Builder : Symbol(Builder, Decl(methodChainError.ts, 0, 0))
.method("a")
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
.method();
>method : Symbol(Builder.method, Decl(methodChainError.ts, 0, 15))
@@ -0,0 +1,28 @@
=== tests/cases/compiler/methodChainError.ts ===
class Builder {
>Builder : Builder
method(param: string): Builder {
>method : (param: string) => Builder
>param : string
return this;
>this : this
}
}
new Builder()
>new Builder() .method("a") .method() : Builder
>new Builder() .method("a") .method : (param: string) => Builder
>new Builder() .method("a") : Builder
>new Builder() .method : (param: string) => Builder
>new Builder() : Builder
>Builder : typeof Builder
.method("a")
>method : (param: string) => Builder
>"a" : "a"
.method();
>method : (param: string) => Builder
+9
View File
@@ -0,0 +1,9 @@
class Builder {
method(param: string): Builder {
return this;
}
}
new Builder()
.method("a")
.method();