Skip to content

Commit 02288a4

Browse files
authored
Merge pull request #41001 from weswigham/overload-impl-elab
Add elaboration when call fails all overloads but succeeds against the implementation signature
2 parents 23c5f92 + f324fde commit 02288a4

25 files changed

+172
-14
lines changed

src/compiler/builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ namespace ts {
274274
result.relatedInformation = relatedInformation ?
275275
relatedInformation.length ?
276276
relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram, toPath)) :
277-
emptyArray :
277+
[] :
278278
undefined;
279279
return result;
280280
});
@@ -824,7 +824,7 @@ namespace ts {
824824
result.relatedInformation = relatedInformation ?
825825
relatedInformation.length ?
826826
relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r, relativeToBuildInfo)) :
827-
emptyArray :
827+
[] :
828828
undefined;
829829
return result;
830830
});

src/compiler/checker.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27227,10 +27227,10 @@ namespace ts {
2722727227
// is just important for choosing the best signature. So in the case where there is only one
2722827228
// signature, the subtype pass is useless. So skipping it is an optimization.
2722927229
if (candidates.length > 1) {
27230-
result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
27230+
result = chooseOverload(candidates, subtypeRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
2723127231
}
2723227232
if (!result) {
27233-
result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
27233+
result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
2723427234
}
2723527235
if (result) {
2723627236
return result;
@@ -27255,6 +27255,7 @@ namespace ts {
2725527255
if (last.declaration && candidatesForArgumentError.length > 3) {
2725627256
addRelatedInfo(d, createDiagnosticForNode(last.declaration, Diagnostics.The_last_overload_is_declared_here));
2725727257
}
27258+
addImplementationSuccessElaboration(last, d);
2725827259
diagnostics.add(d);
2725927260
}
2726027261
}
@@ -27290,14 +27291,19 @@ namespace ts {
2729027291
const chain = chainDiagnosticMessages(
2729127292
map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText),
2729227293
Diagnostics.No_overload_matches_this_call);
27293-
const related = flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[];
27294+
// 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
27295+
// arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic
27296+
const related = [...flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]];
27297+
let diag: Diagnostic;
2729427298
if (every(diags, d => d.start === diags[0].start && d.length === diags[0].length && d.file === diags[0].file)) {
2729527299
const { file, start, length } = diags[0];
27296-
diagnostics.add({ file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related });
27300+
diag = { file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related };
2729727301
}
2729827302
else {
27299-
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chain, related));
27303+
diag = createDiagnosticForNodeFromMessageChain(node, chain, related);
2730027304
}
27305+
addImplementationSuccessElaboration(candidatesForArgumentError[0], diag);
27306+
diagnostics.add(diag);
2730127307
}
2730227308
}
2730327309
else if (candidateForArgumentArityError) {
@@ -27322,7 +27328,28 @@ namespace ts {
2732227328

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

27325-
function chooseOverload(candidates: Signature[], relation: ESMap<string, RelationComparisonResult>, signatureHelpTrailingComma = false) {
27331+
function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) {
27332+
const oldCandidatesForArgumentError = candidatesForArgumentError;
27333+
const oldCandidateForArgumentArityError = candidateForArgumentArityError;
27334+
const oldCandidateForTypeArgumentError = candidateForTypeArgumentError;
27335+
27336+
const declCount = length(failed.declaration?.symbol.declarations);
27337+
const isOverload = declCount > 1;
27338+
const implDecl = isOverload ? find(failed.declaration?.symbol.declarations || emptyArray, d => nodeIsPresent((d as FunctionLikeDeclaration).body)) : undefined;
27339+
if (implDecl) {
27340+
const candidate = getSignatureFromDeclaration(implDecl as FunctionLikeDeclaration);
27341+
const isSingleNonGenericCandidate = !candidate.typeParameters;
27342+
if (chooseOverload([candidate], assignableRelation, isSingleNonGenericCandidate)) {
27343+
addRelatedInfo(diagnostic, createDiagnosticForNode(implDecl, Diagnostics.The_call_would_have_succeeded_against_this_implementation_but_implementation_signatures_of_overloads_are_not_externally_visible));
27344+
}
27345+
}
27346+
27347+
candidatesForArgumentError = oldCandidatesForArgumentError;
27348+
candidateForArgumentArityError = oldCandidateForArgumentArityError;
27349+
candidateForTypeArgumentError = oldCandidateForTypeArgumentError;
27350+
}
27351+
27352+
function chooseOverload(candidates: Signature[], relation: ESMap<string, RelationComparisonResult>, isSingleNonGenericCandidate: boolean, signatureHelpTrailingComma = false) {
2732627353
candidatesForArgumentError = undefined;
2732727354
candidateForArgumentArityError = undefined;
2732827355
candidateForTypeArgumentError = undefined;

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3043,7 +3043,10 @@
30433043
"category": "Error",
30443044
"code": 2792
30453045
},
3046-
3046+
"The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.": {
3047+
"category": "Error",
3048+
"code": 2793
3049+
},
30473050
"Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": {
30483051
"category": "Error",
30493052
"code": 2794

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6653,6 +6653,7 @@ namespace ts {
66536653
if (!diagnostic.relatedInformation) {
66546654
diagnostic.relatedInformation = [];
66556655
}
6656+
Debug.assert(diagnostic.relatedInformation !== emptyArray, "Diagnostic had empty array singleton for related info, but is still being constructed!");
66566657
diagnostic.relatedInformation.push(...relatedInformation);
66576658
return diagnostic;
66586659
}

tests/baselines/reference/constructorOverloads1.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ tests/cases/compiler/constructorOverloads1.ts(17,18): error TS2769: No overload
4949
!!! error TS2769: Argument of type 'Foo' is not assignable to parameter of type 'string'.
5050
!!! error TS2769: Overload 2 of 2, '(n: number): Foo', gave the following error.
5151
!!! error TS2769: Argument of type 'Foo' is not assignable to parameter of type 'number'.
52+
!!! 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.
5253
var f4 = new Foo([f1,f2,f3]);
5354
~~~~~~~~~~
5455
!!! error TS2769: No overload matches this call.
5556
!!! error TS2769: Overload 1 of 2, '(s: string): Foo', gave the following error.
5657
!!! error TS2769: Argument of type 'Foo[]' is not assignable to parameter of type 'string'.
5758
!!! error TS2769: Overload 2 of 2, '(n: number): Foo', gave the following error.
5859
!!! error TS2769: Argument of type 'Foo[]' is not assignable to parameter of type 'number'.
60+
!!! 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.
5961

6062
f1.bar1();
6163
f1.bar2();

tests/baselines/reference/functionOverloads2.errors.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ tests/cases/compiler/functionOverloads2.ts(4,13): error TS2769: No overload matc
1515
!!! error TS2769: Overload 1 of 2, '(bar: string): string', gave the following error.
1616
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'string'.
1717
!!! error TS2769: Overload 2 of 2, '(bar: number): number', gave the following error.
18-
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'number'.
18+
!!! error TS2769: Argument of type 'boolean' is not assignable to parameter of type 'number'.
19+
!!! 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.

tests/baselines/reference/functionOverloads27.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ tests/cases/compiler/functionOverloads27.ts(4,13): error TS2345: Argument of typ
88
var x = foo(5);
99
~
1010
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
11+
!!! 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.
1112

tests/baselines/reference/functionOverloads40.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ tests/cases/compiler/functionOverloads40.ts(4,15): error TS2769: No overload mat
1818
!!! error TS2769: Type 'string' is not assignable to type 'boolean'.
1919
!!! related TS6500 tests/cases/compiler/functionOverloads40.ts:1:19: The expected type comes from property 'a' which is declared here on type '{ a: number; }'
2020
!!! related TS6500 tests/cases/compiler/functionOverloads40.ts:2:19: The expected type comes from property 'a' which is declared here on type '{ a: boolean; }'
21+
!!! 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.
2122

tests/baselines/reference/heterogeneousArrayAndOverloads.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ tests/cases/compiler/heterogeneousArrayAndOverloads.ts(9,26): error TS2769: No o
1717
!!! error TS2769: No overload matches this call.
1818
!!! error TS2769: Overload 1 of 2, '(arg1: number[]): any', gave the following error.
1919
!!! error TS2769: Type 'string' is not assignable to type 'number'.
20+
!!! 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.
2021
}
2122
}

tests/baselines/reference/incompatibleTypes.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ tests/cases/compiler/incompatibleTypes.ts(74,5): error TS2322: Type '(a: any) =>
9999
!!! error TS2769: Argument of type 'C1' is not assignable to parameter of type 'IFoo2'.
100100
!!! error TS2769: The types returned by 'p1(...)' are incompatible between these types.
101101
!!! error TS2769: Type 'string' is not assignable to type 'number'.
102+
!!! 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.
102103

103104

104105
function of1(n: { a: { a: string; }; b: string; }): number;
@@ -114,6 +115,7 @@ tests/cases/compiler/incompatibleTypes.ts(74,5): error TS2322: Type '(a: any) =>
114115
!!! error TS2769: Overload 2 of 2, '(s: { c: { b: string; }; d: string; }): string', gave the following error.
115116
!!! error TS2769: Argument of type '{ e: number; f: number; }' is not assignable to parameter of type '{ c: { b: string; }; d: string; }'.
116117
!!! error TS2769: Object literal may only specify known properties, and 'e' does not exist in type '{ c: { b: string; }; d: string; }'.
118+
!!! 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.
117119

118120
interface IMap {
119121
[key:string]:string;

tests/baselines/reference/optionalBindingParametersInOverloads1.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads1.
1313
foo([false, 0, ""]);
1414
~~~~~
1515
!!! error TS2322: Type 'boolean' is not assignable to type 'string'.
16+
!!! 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.
1617
~~
17-
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
18+
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
19+
!!! 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.

tests/baselines/reference/optionalBindingParametersInOverloads2.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ tests/cases/conformance/es6/destructuring/optionalBindingParametersInOverloads2.
1414
~
1515
!!! error TS2322: Type 'boolean' is not assignable to type 'string'.
1616
!!! 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; }'
17+
!!! 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.
1718
~
1819
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
19-
!!! 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; }'
20+
!!! 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; }'
21+
!!! 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.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts(8,12): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
2+
3+
4+
==== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts (1 errors) ====
5+
class EventAggregator
6+
{
7+
publish(event: string, data?: any): void;
8+
publish<T>(event: T): void {}
9+
}
10+
11+
var ea: EventAggregator;
12+
ea.publish([1,2,3]);
13+
~~~~~~~
14+
!!! error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
15+
!!! 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.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [overloadErrorMatchesImplementationElaboaration.ts]
2+
class EventAggregator
3+
{
4+
publish(event: string, data?: any): void;
5+
publish<T>(event: T): void {}
6+
}
7+
8+
var ea: EventAggregator;
9+
ea.publish([1,2,3]);
10+
11+
//// [overloadErrorMatchesImplementationElaboaration.js]
12+
var EventAggregator = /** @class */ (function () {
13+
function EventAggregator() {
14+
}
15+
EventAggregator.prototype.publish = function (event) { };
16+
return EventAggregator;
17+
}());
18+
var ea;
19+
ea.publish([1, 2, 3]);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts ===
2+
class EventAggregator
3+
>EventAggregator : Symbol(EventAggregator, Decl(overloadErrorMatchesImplementationElaboaration.ts, 0, 0))
4+
{
5+
publish(event: string, data?: any): void;
6+
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
7+
>event : Symbol(event, Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 12))
8+
>data : Symbol(data, Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 26))
9+
10+
publish<T>(event: T): void {}
11+
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
12+
>T : Symbol(T, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 12))
13+
>event : Symbol(event, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 15))
14+
>T : Symbol(T, Decl(overloadErrorMatchesImplementationElaboaration.ts, 3, 12))
15+
}
16+
17+
var ea: EventAggregator;
18+
>ea : Symbol(ea, Decl(overloadErrorMatchesImplementationElaboaration.ts, 6, 3))
19+
>EventAggregator : Symbol(EventAggregator, Decl(overloadErrorMatchesImplementationElaboaration.ts, 0, 0))
20+
21+
ea.publish([1,2,3]);
22+
>ea.publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
23+
>ea : Symbol(ea, Decl(overloadErrorMatchesImplementationElaboaration.ts, 6, 3))
24+
>publish : Symbol(EventAggregator.publish, Decl(overloadErrorMatchesImplementationElaboaration.ts, 1, 1), Decl(overloadErrorMatchesImplementationElaboaration.ts, 2, 45))
25+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/compiler/overloadErrorMatchesImplementationElaboaration.ts ===
2+
class EventAggregator
3+
>EventAggregator : EventAggregator
4+
{
5+
publish(event: string, data?: any): void;
6+
>publish : (event: string, data?: any) => void
7+
>event : string
8+
>data : any
9+
10+
publish<T>(event: T): void {}
11+
>publish : (event: string, data?: any) => void
12+
>event : T
13+
}
14+
15+
var ea: EventAggregator;
16+
>ea : EventAggregator
17+
18+
ea.publish([1,2,3]);
19+
>ea.publish([1,2,3]) : void
20+
>ea.publish : (event: string, data?: any) => void
21+
>ea : EventAggregator
22+
>publish : (event: string, data?: any) => void
23+
>[1,2,3] : number[]
24+
>1 : 1
25+
>2 : 2
26+
>3 : 3
27+

tests/baselines/reference/overloadOnConstNoAnyImplementation2.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ tests/cases/compiler/overloadOnConstNoAnyImplementation2.ts(21,9): error TS2345:
3636
!!! error TS2345: Argument of type '(x: 'bye') => number' is not assignable to parameter of type '(x: "hi") => number'.
3737
!!! error TS2345: Types of parameters 'x' and 'x' are incompatible.
3838
!!! error TS2345: Type '"hi"' is not assignable to type '"bye"'.
39+
!!! 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.
3940
c.x1(1, (x) => { return 1; } );
4041

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

0 commit comments

Comments
 (0)