From 6b2ea463b2251d0452029f4b108e502d7f3030f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 11 Sep 2018 14:35:01 +0800 Subject: [PATCH 1/2] improve Diagnostics for accidentally calling type-assertion expressions --- src/compiler/checker.ts | 7 +++ src/compiler/diagnosticMessages.json | 4 ++ ...CallingTypeAssertionExpressions.errors.txt | 35 ++++++++++++++ ...dentallyCallingTypeAssertionExpressions.js | 19 ++++++++ ...llyCallingTypeAssertionExpressions.symbols | 20 ++++++++ ...tallyCallingTypeAssertionExpressions.types | 48 +++++++++++++++++++ ...dentallyCallingTypeAssertionExpressions.ts | 11 +++++ 7 files changed, 144 insertions(+) create mode 100644 tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt create mode 100644 tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js create mode 100644 tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols create mode 100644 tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types create mode 100644 tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ae0124616a418..2b146e230ffda 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19655,6 +19655,13 @@ namespace ts { error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); } else { + if (node.arguments.length === 1 && isTypeAssertion(first(node.arguments))) { + const text = getSourceFileOfNode(node).text; + const pos = skipTrivia(text, node.expression.end, /* stepAfterLineBreak */ true) - 1; + if (isLineBreak(text.charCodeAt(pos))) { + error(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon); + } + } invocationError(node, apparentType, SignatureKind.Call); } return resolveErrorCall(node); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 4384167a4e088..399afb4ebc675 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2457,6 +2457,10 @@ "category": "Error", "code": 2733 }, + "It is highly likely that you are missing a semicolon.": { + "category": "Error", + "code": 2734 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt new file mode 100644 index 0000000000000..b27c6b06d489c --- /dev/null +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt @@ -0,0 +1,35 @@ +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(3,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(5,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(7,1): error TS2734: It is highly likely that you are missing a semicolon. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(7,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(10,1): error TS2734: It is highly likely that you are missing a semicolon. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(10,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. + + +==== tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts (6 errors) ==== + declare function foo(): string; + + foo()(1 as number).toString(); + ~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. + + foo() (1 as number).toString(); + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. + + foo() + ~~~~~ +!!! error TS2734: It is highly likely that you are missing a semicolon. + ~~~~~ + (1 as number).toString(); + ~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. + + foo() + ~~~~~ +!!! error TS2734: It is highly likely that you are missing a semicolon. + ~~~~~~~~ + (1 as number).toString(); + ~~~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. + \ No newline at end of file diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js new file mode 100644 index 0000000000000..ff22844b48b1a --- /dev/null +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js @@ -0,0 +1,19 @@ +//// [betterErrorForAccidentallyCallingTypeAssertionExpressions.ts] +declare function foo(): string; + +foo()(1 as number).toString(); + +foo() (1 as number).toString(); + +foo() +(1 as number).toString(); + +foo() + (1 as number).toString(); + + +//// [betterErrorForAccidentallyCallingTypeAssertionExpressions.js] +foo()(1).toString(); +foo()(1).toString(); +foo()(1).toString(); +foo()(1).toString(); diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols new file mode 100644 index 0000000000000..fb49ecd070a58 --- /dev/null +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts === +declare function foo(): string; +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + +foo()(1 as number).toString(); +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + +foo() (1 as number).toString(); +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + +foo() +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + +(1 as number).toString(); + +foo() +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + + (1 as number).toString(); + diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types new file mode 100644 index 0000000000000..a9569a9dadf25 --- /dev/null +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types @@ -0,0 +1,48 @@ +=== tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts === +declare function foo(): string; +>foo : () => string + +foo()(1 as number).toString(); +>foo()(1 as number).toString() : any +>foo()(1 as number).toString : any +>foo()(1 as number) : any +>foo() : string +>foo : () => string +>1 as number : number +>1 : 1 +>toString : any + +foo() (1 as number).toString(); +>foo() (1 as number).toString() : any +>foo() (1 as number).toString : any +>foo() (1 as number) : any +>foo() : string +>foo : () => string +>1 as number : number +>1 : 1 +>toString : any + +foo() +>foo()(1 as number).toString() : any +>foo()(1 as number).toString : any +>foo()(1 as number) : any +>foo() : string +>foo : () => string + +(1 as number).toString(); +>1 as number : number +>1 : 1 +>toString : any + +foo() +>foo() (1 as number).toString() : any +>foo() (1 as number).toString : any +>foo() (1 as number) : any +>foo() : string +>foo : () => string + + (1 as number).toString(); +>1 as number : number +>1 : 1 +>toString : any + diff --git a/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts b/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts new file mode 100644 index 0000000000000..957dc5cab75e6 --- /dev/null +++ b/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts @@ -0,0 +1,11 @@ +declare function foo(): string; + +foo()(1 as number).toString(); + +foo() (1 as number).toString(); + +foo() +(1 as number).toString(); + +foo() + (1 as number).toString(); From 2cf2bbd5f77a7756e6861fd2942e68e279046b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 11 Sep 2018 16:20:38 +0800 Subject: [PATCH 2/2] improve test case and add related diagnostic --- src/compiler/checker.ts | 19 ++++++++++--------- ...CallingTypeAssertionExpressions.errors.txt | 18 +++++++++++------- ...dentallyCallingTypeAssertionExpressions.js | 4 ++++ ...llyCallingTypeAssertionExpressions.symbols | 5 +++++ ...tallyCallingTypeAssertionExpressions.types | 12 ++++++++++++ ...dentallyCallingTypeAssertionExpressions.ts | 3 +++ 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b146e230ffda..96e72a9e07ed9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19655,14 +19655,14 @@ namespace ts { error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); } else { + let relatedInformation: DiagnosticRelatedInformation | undefined; if (node.arguments.length === 1 && isTypeAssertion(first(node.arguments))) { const text = getSourceFileOfNode(node).text; - const pos = skipTrivia(text, node.expression.end, /* stepAfterLineBreak */ true) - 1; - if (isLineBreak(text.charCodeAt(pos))) { - error(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon); + if (isLineBreak(text.charCodeAt(skipTrivia(text, node.expression.end, /* stopAfterLineBreak */ true) - 1))) { + relatedInformation = createDiagnosticForNode(node.expression, Diagnostics.It_is_highly_likely_that_you_are_missing_a_semicolon); } } - invocationError(node, apparentType, SignatureKind.Call); + invocationError(node, apparentType, SignatureKind.Call, relatedInformation); } return resolveErrorCall(node); } @@ -19832,11 +19832,12 @@ namespace ts { return true; } - function invocationError(node: Node, apparentType: Type, kind: SignatureKind) { - invocationErrorRecovery(apparentType, kind, error(node, kind === SignatureKind.Call - ? Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures - : Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature - , typeToString(apparentType))); + function invocationError(node: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) { + const diagnostic = error(node, (kind === SignatureKind.Call ? + Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures : + Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature + ), typeToString(apparentType)); + invocationErrorRecovery(apparentType, kind, relatedInformation ? addRelatedInfo(diagnostic, relatedInformation) : diagnostic); } function invocationErrorRecovery(apparentType: Type, kind: SignatureKind, diagnostic: Diagnostic) { diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt index b27c6b06d489c..023e40a70dad3 100644 --- a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.errors.txt @@ -1,12 +1,11 @@ tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(3,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(5,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. -tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(7,1): error TS2734: It is highly likely that you are missing a semicolon. tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(7,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. -tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(10,1): error TS2734: It is highly likely that you are missing a semicolon. tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(10,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts(13,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. -==== tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts (6 errors) ==== +==== tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts (5 errors) ==== declare function foo(): string; foo()(1 as number).toString(); @@ -19,17 +18,22 @@ tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.t foo() ~~~~~ -!!! error TS2734: It is highly likely that you are missing a semicolon. - ~~~~~ (1 as number).toString(); ~~~~~~~~~~~~~ !!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +!!! related TS2734 tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts:7:1: It is highly likely that you are missing a semicolon. foo() - ~~~~~ -!!! error TS2734: It is highly likely that you are missing a semicolon. ~~~~~~~~ (1 as number).toString(); ~~~~~~~~~~~~~~~~~ !!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +!!! related TS2734 tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts:10:1: It is highly likely that you are missing a semicolon. + + foo() + ~~~~~~~~ + (1).toString(); + ~~~~~~~~~~~~~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures. +!!! related TS2734 tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts:13:1: It is highly likely that you are missing a semicolon. \ No newline at end of file diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js index ff22844b48b1a..877ed539e7175 100644 --- a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.js @@ -10,6 +10,9 @@ foo() foo() (1 as number).toString(); + +foo() + (1).toString(); //// [betterErrorForAccidentallyCallingTypeAssertionExpressions.js] @@ -17,3 +20,4 @@ foo()(1).toString(); foo()(1).toString(); foo()(1).toString(); foo()(1).toString(); +foo()(1).toString(); diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols index fb49ecd070a58..9dc2e6769372b 100644 --- a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.symbols @@ -18,3 +18,8 @@ foo() (1 as number).toString(); +foo() +>foo : Symbol(foo, Decl(betterErrorForAccidentallyCallingTypeAssertionExpressions.ts, 0, 0)) + + (1).toString(); + diff --git a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types index a9569a9dadf25..54564d7462c50 100644 --- a/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types +++ b/tests/baselines/reference/betterErrorForAccidentallyCallingTypeAssertionExpressions.types @@ -46,3 +46,15 @@ foo() >1 : 1 >toString : any +foo() +>foo() (1).toString() : any +>foo() (1).toString : any +>foo() (1) : any +>foo() : string +>foo : () => string + + (1).toString(); +>1 : number +>1 : 1 +>toString : any + diff --git a/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts b/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts index 957dc5cab75e6..42c3025c8e3f6 100644 --- a/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts +++ b/tests/cases/compiler/betterErrorForAccidentallyCallingTypeAssertionExpressions.ts @@ -9,3 +9,6 @@ foo() foo() (1 as number).toString(); + +foo() + (1).toString();