Skip to content

Add elaboration when call fails all overloads but succeeds against the implementation signature #41001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ namespace ts {
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
emptyArray :
[] :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did we ever figure out what the performance impact of [] vs emptyArray is? Last I heard is that [] is better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...eh? In this case, it's just because we mutate relatedInformation in some places, so using a global singleton is bad.

undefined;
return result;
});
Expand Down Expand Up @@ -824,7 +824,7 @@ namespace ts {
result.relatedInformation = relatedInformation ?
relatedInformation.length ?
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
emptyArray :
[] :
undefined;
return result;
});
Expand Down
39 changes: 33 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27227,10 +27227,10 @@ namespace ts {
// is just important for choosing the best signature. So in the case where there is only one
// signature, the subtype pass is useless. So skipping it is an optimization.
if (candidates.length > 1) {
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
result = chooseOverload(candidates, subtypeRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
}
if (!result) {
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
}
if (result) {
return result;
Expand All @@ -27255,6 +27255,7 @@ namespace ts {
if (last.declaration && candidatesForArgumentError.length > 3) {
addRelatedInfo(d, createDiagnosticForNode(last.declaration, Diagnostics.The_last_overload_is_declared_here));
}
addImplementationSuccessElaboration(last, d);
diagnostics.add(d);
}
}
Expand Down Expand Up @@ -27290,14 +27291,19 @@ namespace ts {
const chain = chainDiagnosticMessages(
map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText),
Diagnostics.No_overload_matches_this_call);
const related = flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[];
// The below is a spread to guarantee we get a new (mutable) array - our `flatMap` helper tries to do "smart" optimizations where it reuses input
// arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic
const related = [...flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]];
let diag: Diagnostic;
if (every(diags, d => d.start === diags[0].start && d.length === diags[0].length && d.file === diags[0].file)) {
const { file, start, length } = diags[0];
diagnostics.add({ file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related });
diag = { file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related };
}
else {
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chain, related));
diag = createDiagnosticForNodeFromMessageChain(node, chain, related);
}
addImplementationSuccessElaboration(candidatesForArgumentError[0], diag);
diagnostics.add(diag);
}
}
else if (candidateForArgumentArityError) {
Expand All @@ -27322,7 +27328,28 @@ namespace ts {

return getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray);

function chooseOverload(candidates: Signature[], relation: ESMap<string, RelationComparisonResult>, signatureHelpTrailingComma = false) {
function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) {
const oldCandidatesForArgumentError = candidatesForArgumentError;
const oldCandidateForArgumentArityError = candidateForArgumentArityError;
const oldCandidateForTypeArgumentError = candidateForTypeArgumentError;

const declCount = length(failed.declaration?.symbol.declarations);
const isOverload = declCount > 1;
const implDecl = isOverload ? find(failed.declaration?.symbol.declarations || emptyArray, d => nodeIsPresent((d as FunctionLikeDeclaration).body)) : undefined;
if (implDecl) {
const candidate = getSignatureFromDeclaration(implDecl as FunctionLikeDeclaration);
const isSingleNonGenericCandidate = !candidate.typeParameters;
if (chooseOverload([candidate], assignableRelation, isSingleNonGenericCandidate)) {
addRelatedInfo(diagnostic, createDiagnosticForNode(implDecl, Diagnostics.The_call_would_have_succeeded_against_this_implementation_but_implementation_signatures_of_overloads_are_not_externally_visible));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mutate the related list? (and is that why you clone into related above?)

Copy link
Member Author

@weswigham weswigham Oct 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addRelatedInfo, as the name implies, has always appended to the existing list.


candidatesForArgumentError = oldCandidatesForArgumentError;
candidateForArgumentArityError = oldCandidateForArgumentArityError;
candidateForTypeArgumentError = oldCandidateForTypeArgumentError;
}

function chooseOverload(candidates: Signature[], relation: ESMap<string, RelationComparisonResult>, isSingleNonGenericCandidate: boolean, signatureHelpTrailingComma = false) {
candidatesForArgumentError = undefined;
candidateForArgumentArityError = undefined;
candidateForTypeArgumentError = undefined;
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3043,7 +3043,10 @@
"category": "Error",
"code": 2792
},

"The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.": {
"category": "Error",
"code": 2793
},
"Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": {
"category": "Error",
"code": 2794
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6653,6 +6653,7 @@ namespace ts {
if (!diagnostic.relatedInformation) {
diagnostic.relatedInformation = [];
}
Debug.assert(diagnostic.relatedInformation !== emptyArray, "Diagnostic had empty array singleton for related info, but is still being constructed!");
diagnostic.relatedInformation.push(...relatedInformation);
return diagnostic;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/constructorOverloads1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ tests/cases/compiler/constructorOverloads1.ts(17,18): error TS2769: No overload
!!! error TS2769: Argument of type 'Foo' is not assignable to parameter of type 'string'.
!!! error TS2769: Overload 2 of 2, '(n: number): Foo', gave the following error.
!!! error TS2769: Argument of type 'Foo' is not assignable to parameter of type 'number'.
!!! related TS2793 tests/cases/compiler/constructorOverloads1.ts:4:5: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
var f4 = new Foo([f1,f2,f3]);
~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(s: string): Foo', gave the following error.
!!! error TS2769: Argument of type 'Foo[]' is not assignable to parameter of type 'string'.
!!! error TS2769: Overload 2 of 2, '(n: number): Foo', gave the following error.
!!! error TS2769: Argument of type 'Foo[]' is not assignable to parameter of type 'number'.
!!! related TS2793 tests/cases/compiler/constructorOverloads1.ts:4:5: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

f1.bar1();
f1.bar2();
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/functionOverloads2.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ tests/cases/compiler/functionOverloads2.ts(4,13): error TS2769: No overload matc
!!! error TS2769: Overload 1 of 2, '(bar: string): string', gave the following error.
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'string'.
!!! error TS2769: Overload 2 of 2, '(bar: number): number', gave the following error.
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'number'.
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'number'.
!!! related TS2793 tests/cases/compiler/functionOverloads2.ts:3:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

1 change: 1 addition & 0 deletions tests/baselines/reference/functionOverloads27.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ tests/cases/compiler/functionOverloads27.ts(4,13): error TS2345: Argument of typ
var x = foo(5);
~
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
!!! related TS2793 tests/cases/compiler/functionOverloads27.ts:3:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

1 change: 1 addition & 0 deletions tests/baselines/reference/functionOverloads40.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ tests/cases/compiler/functionOverloads40.ts(4,15): error TS2769: No overload mat
!!! error TS2769: Type 'string' is not assignable to type 'boolean'.
!!! related TS6500 tests/cases/compiler/functionOverloads40.ts:1:19: The expected type comes from property 'a' which is declared here on type '{ a: number; }'
!!! related TS6500 tests/cases/compiler/functionOverloads40.ts:2:19: The expected type comes from property 'a' which is declared here on type '{ a: boolean; }'
!!! related TS2793 tests/cases/compiler/functionOverloads40.ts:3:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ tests/cases/compiler/heterogeneousArrayAndOverloads.ts(9,26): error TS2769: No o
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(arg1: number[]): any', gave the following error.
!!! error TS2769: Type 'string' is not assignable to type 'number'.
!!! related TS2793 tests/cases/compiler/heterogeneousArrayAndOverloads.ts:4:5: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
}
}
2 changes: 2 additions & 0 deletions tests/baselines/reference/incompatibleTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ tests/cases/compiler/incompatibleTypes.ts(74,5): error TS2322: Type '(a: any) =>
!!! error TS2769: Argument of type 'C1' is not assignable to parameter of type 'IFoo2'.
!!! error TS2769: The types returned by 'p1(...)' are incompatible between these types.
!!! error TS2769: Type 'string' is not assignable to type 'number'.
!!! related TS2793 tests/cases/compiler/incompatibleTypes.ts:39:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.


function of1(n: { a: { a: string; }; b: string; }): number;
Expand All @@ -114,6 +115,7 @@ tests/cases/compiler/incompatibleTypes.ts(74,5): error TS2322: Type '(a: any) =>
!!! error TS2769: Overload 2 of 2, '(s: { c: { b: string; }; d: string; }): string', gave the following error.
!!! error TS2769: Argument of type '{ e: number; f: number; }' is not assignable to parameter of type '{ c: { b: string; }; d: string; }'.
!!! error TS2769: Object literal may only specify known properties, and 'e' does not exist in type '{ c: { b: string; }; d: string; }'.
!!! related TS2793 tests/cases/compiler/incompatibleTypes.ts:47:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

interface IMap {
[key:string]:string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.
foo([false, 0, ""]);
~~~~~
!!! error TS2322: Type 'boolean' is not assignable to type 'string'.
!!! related TS2793 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.ts:2:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
~~
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
!!! related TS2793 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.ts:2:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.
~
!!! error TS2322: Type 'boolean' is not assignable to type 'string'.
!!! related TS6500 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.ts:1:30: The expected type comes from property 'x' which is declared here on type '{ x: string; y: number; z: boolean; }'
!!! related TS2793 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.ts:2:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
~
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
!!! related TS6500 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.ts:1:52: The expected type comes from property 'z' which is declared here on type '{ x: string; y: number; z: boolean; }'
!!! related TS6500 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.ts:1:52: The expected type comes from property 'z' which is declared here on type '{ x: string; y: number; z: boolean; }'
!!! related TS2793 tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.ts:2:10: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts(8,12): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.


==== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts (1 errors) ====
class EventAggregator
{
publish(event: string, data?: any): void;
publish<T>(event: T): void {}
}

var ea: EventAggregator;
ea.publish([1,2,3]);
~~~~~~~
!!! error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
!!! related TS2793 tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts:4:5: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [overloadErrorMatchesImplementationElaboaration.ts]
class EventAggregator
{
publish(event: string, data?: any): void;
publish<T>(event: T): void {}
}

var ea: EventAggregator;
ea.publish([1,2,3]);

//// [overloadErrorMatchesImplementationElaboaration.js]
var EventAggregator = /** @class */ (function () {
function EventAggregator() {
}
EventAggregator.prototype.publish = function (event) { };
return EventAggregator;
}());
var ea;
ea.publish([1, 2, 3]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts ===
class EventAggregator
>EventAggregator : Symbol(EventAggregator, Decl(overloadErrorMatchesImplementationElaboaration.ts, 0, 0))
{
publish(event: string, data?: any): void;
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
>event : Symbol(event, Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 12))
>data : Symbol(data, Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 26))

publish<T>(event: T): void {}
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
>T : Symbol(T, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 12))
>event : Symbol(event, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 15))
>T : Symbol(T, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 12))
}

var ea: EventAggregator;
>ea : Symbol(ea, Decl(overloadErrorMatchesImplementationElaboaration.ts, 6, 3))
>EventAggregator : Symbol(EventAggregator, Decl(overloadErrorMatchesImplementationElaboaration.ts, 0, 0))

ea.publish([1,2,3]);
>ea.publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
>ea : Symbol(ea, Decl(overloadErrorMatchesImplementationElaboaration.ts, 6, 3))
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
=== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts ===
class EventAggregator
>EventAggregator : EventAggregator
{
publish(event: string, data?: any): void;
>publish : (event: string, data?: any) => void
>event : string
>data : any

publish<T>(event: T): void {}
>publish : (event: string, data?: any) => void
>event : T
}

var ea: EventAggregator;
>ea : EventAggregator

ea.publish([1,2,3]);
>ea.publish([1,2,3]) : void
>ea.publish : (event: string, data?: any) => void
>ea : EventAggregator
>publish : (event: string, data?: any) => void
>[1,2,3] : number[]
>1 : 1
>2 : 2
>3 : 3

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ tests/cases/compiler/overloadOnConstNoAnyImplementation2.ts(21,9): error TS2345:
!!! error TS2345: Argument of type '(x: 'bye') => number' is not assignable to parameter of type '(x: "hi") => number'.
!!! error TS2345: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2345: Type '"hi"' is not assignable to type '"bye"'.
!!! related TS2793 tests/cases/compiler/overloadOnConstNoAnyImplementation2.ts:7:5: The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
c.x1(1, (x) => { return 1; } );

c.x1(1, (x: number) => { return 1; } );
Expand Down
Loading