From e9b48e78c7ba51c7722aeef574548e3e0fe7a327 Mon Sep 17 00:00:00 2001 From: Dhruv Rajvanshi Date: Thu, 16 May 2019 01:37:24 +0530 Subject: [PATCH] Improve error spans on chained method calls --- src/compiler/checker.ts | 20 +++++++++---- .../reference/methodChainError.errors.txt | 16 +++++++++++ tests/baselines/reference/methodChainError.js | 23 +++++++++++++++ .../reference/methodChainError.symbols | 25 +++++++++++++++++ .../reference/methodChainError.types | 28 +++++++++++++++++++ tests/cases/compiler/methodChainError.ts | 9 ++++++ 6 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/methodChainError.errors.txt create mode 100644 tests/baselines/reference/methodChainError.js create mode 100644 tests/baselines/reference/methodChainError.symbols create mode 100644 tests/baselines/reference/methodChainError.types create mode 100644 tests/cases/compiler/methodChainError.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f5673af3ee..cfe7a7cdec2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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, signatureHelpTrailingComma = false) { candidateForArgumentError = undefined; candidateForArgumentArityError = undefined; diff --git a/tests/baselines/reference/methodChainError.errors.txt b/tests/baselines/reference/methodChainError.errors.txt new file mode 100644 index 00000000000..7cd4df95d40 --- /dev/null +++ b/tests/baselines/reference/methodChainError.errors.txt @@ -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. \ No newline at end of file diff --git a/tests/baselines/reference/methodChainError.js b/tests/baselines/reference/methodChainError.js new file mode 100644 index 00000000000..648ae505c7a --- /dev/null +++ b/tests/baselines/reference/methodChainError.js @@ -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(); diff --git a/tests/baselines/reference/methodChainError.symbols b/tests/baselines/reference/methodChainError.symbols new file mode 100644 index 00000000000..e73024c784d --- /dev/null +++ b/tests/baselines/reference/methodChainError.symbols @@ -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)) + diff --git a/tests/baselines/reference/methodChainError.types b/tests/baselines/reference/methodChainError.types new file mode 100644 index 00000000000..e66f6c54b45 --- /dev/null +++ b/tests/baselines/reference/methodChainError.types @@ -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 + diff --git a/tests/cases/compiler/methodChainError.ts b/tests/cases/compiler/methodChainError.ts new file mode 100644 index 00000000000..657d9a634a5 --- /dev/null +++ b/tests/cases/compiler/methodChainError.ts @@ -0,0 +1,9 @@ +class Builder { + method(param: string): Builder { + return this; + } +} + +new Builder() + .method("a") + .method(); \ No newline at end of file