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;

0 commit comments

Comments
 (0)