Skip to content

Commit 0ec894f

Browse files
committed
Improved diagnostic for the situation where overload matching fails to find any applicable overloads. This addresses #6069.
1 parent 0cb2e5d commit 0ec894f

File tree

7 files changed

+42
-12
lines changed

7 files changed

+42
-12
lines changed

packages/pyright-internal/src/analyzer/typeEvaluator.ts

+30-3
Original file line numberDiff line numberDiff line change
@@ -8887,7 +8887,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
88878887

88888888
// Create a helper function that evaluates the overload that best
88898889
// matches the arg/param lists.
8890-
function evaluateUsingBestMatchingOverload(skipUnknownArgCheck: boolean) {
8890+
function evaluateUsingBestMatchingOverload(skipUnknownArgCheck: boolean, emitNoOverloadFoundError: boolean) {
88918891
// Find the match with the smallest argument match score. If there
88928892
// are more than one with the same score, use the one with the
88938893
// largest index. Later overloads tend to be more general.
@@ -8898,6 +8898,27 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
88988898
return current.argumentMatchScore < previous.argumentMatchScore ? current : previous;
88998899
});
89008900

8901+
// If there is more than one filtered match, report that no match
8902+
// was possible and emit a diagnostic that provides the most likely.
8903+
if (emitNoOverloadFoundError) {
8904+
const functionName = bestMatch.overload.details.name || '<anonymous function>';
8905+
const diagnostic = addDiagnostic(
8906+
AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
8907+
DiagnosticRule.reportGeneralTypeIssues,
8908+
Localizer.Diagnostic.noOverload().format({ name: functionName }),
8909+
errorNode
8910+
);
8911+
8912+
const overrideDecl = bestMatch.overload.details.declaration;
8913+
if (diagnostic && overrideDecl) {
8914+
diagnostic.addRelatedInfo(
8915+
Localizer.DiagnosticAddendum.overloadIndex().format({ index: bestMatch.overloadIndex + 1 }),
8916+
overrideDecl.path,
8917+
overrideDecl.range
8918+
);
8919+
}
8920+
}
8921+
89018922
const effectiveTypeVarContext = typeVarContext ?? new TypeVarContext();
89028923
effectiveTypeVarContext.addSolveForScope(getTypeVarScopeIds(bestMatch.overload));
89038924
effectiveTypeVarContext.unlock();
@@ -8915,7 +8936,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
89158936
// use the normal type matching mechanism because it is faster and
89168937
// will provide a clearer error message.
89178938
if (filteredMatchResults.length === 1) {
8918-
return evaluateUsingBestMatchingOverload(/* skipUnknownArgCheck */ false);
8939+
return evaluateUsingBestMatchingOverload(
8940+
/* skipUnknownArgCheck */ false,
8941+
/* emitNoOverloadFoundError */ false
8942+
);
89198943
}
89208944

89218945
let expandedArgTypes: (Type | undefined)[][] | undefined = [argList.map((arg) => undefined)];
@@ -8974,7 +8998,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
89748998
// in speculative mode because it's very expensive, and we're going to
89758999
// suppress the diagnostic anyway.
89769000
if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) {
8977-
const result = evaluateUsingBestMatchingOverload(/* skipUnknownArgCheck */ true);
9001+
const result = evaluateUsingBestMatchingOverload(
9002+
/* skipUnknownArgCheck */ true,
9003+
/* emitNoOverloadFoundError */ true
9004+
);
89789005

89799006
// Replace the result with an unknown type since we don't know
89809007
// what overload should have been used.

packages/pyright-internal/src/localization/localize.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,8 @@ export namespace Localizer {
12491249
new ParameterizedString<{ type: string }>(getRawString('DiagnosticAddendum.noOverloadAssignable'));
12501250
export const orPatternMissingName = () =>
12511251
new ParameterizedString<{ name: string }>(getRawString('DiagnosticAddendum.orPatternMissingName'));
1252+
export const overloadIndex = () =>
1253+
new ParameterizedString<{ index: number }>(getRawString('DiagnosticAddendum.overloadIndex'));
12521254
export const overloadSignature = () => getRawString('DiagnosticAddendum.overloadSignature');
12531255
export const overloadNotAssignable = () =>
12541256
new ParameterizedString<{ name: string }>(getRawString('DiagnosticAddendum.overloadNotAssignable'));

packages/pyright-internal/src/localization/package.nls.en-us.json

+1
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@
633633
"newMethodSignature": "Signature of __new__ is \"{type}\"",
634634
"noOverloadAssignable": "No overloaded function matches type \"{type}\"",
635635
"orPatternMissingName": "Missing names: {name}",
636+
"overloadIndex": "Overload {index} is the closest match",
636637
"overloadSignature": "Overload signature is defined here",
637638
"overloadNotAssignable": "One or more overloads of \"{name}\" is not assignable",
638639
"overriddenMethod": "Overridden method",

packages/pyright-internal/src/tests/typeEvaluator1.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ test('Tuple6', () => {
12751275
test('Tuple7', () => {
12761276
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuple7.py']);
12771277

1278-
TestUtils.validateResults(analysisResults, 1);
1278+
TestUtils.validateResults(analysisResults, 2);
12791279
});
12801280

12811281
test('Tuple8', () => {

packages/pyright-internal/src/tests/typeEvaluator2.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ test('Assignment1', () => {
7575
test('Assignment2', () => {
7676
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['assignment2.py']);
7777

78-
TestUtils.validateResults(analysisResults, 2);
78+
TestUtils.validateResults(analysisResults, 3);
7979
});
8080

8181
test('Assignment3', () => {
@@ -1389,7 +1389,7 @@ test('TypedDict11', () => {
13891389
test('TypedDict12', () => {
13901390
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDict12.py']);
13911391

1392-
TestUtils.validateResults(analysisResults, 6);
1392+
TestUtils.validateResults(analysisResults, 7);
13931393
});
13941394

13951395
test('TypedDict13', () => {

packages/pyright-internal/src/tests/typeEvaluator3.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ test('TypePromotions1', () => {
977977
test('Index1', () => {
978978
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['index1.py']);
979979

980-
TestUtils.validateResults(analysisResults, 7);
980+
TestUtils.validateResults(analysisResults, 10);
981981
});
982982

983983
test('ProtocolModule2', () => {
@@ -1633,7 +1633,7 @@ test('Subscript1', () => {
16331633

16341634
test('Subscript2', () => {
16351635
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['subscript2.py']);
1636-
TestUtils.validateResults(analysisResults, 5);
1636+
TestUtils.validateResults(analysisResults, 8);
16371637
});
16381638

16391639
test('Subscript3', () => {
@@ -1642,7 +1642,7 @@ test('Subscript3', () => {
16421642
// Analyze with Python 3.9 settings.
16431643
configOptions.defaultPythonVersion = PythonVersion.V3_9;
16441644
const analysisResults39 = TestUtils.typeAnalyzeSampleFiles(['subscript3.py'], configOptions);
1645-
TestUtils.validateResults(analysisResults39, 32);
1645+
TestUtils.validateResults(analysisResults39, 37);
16461646

16471647
// Analyze with Python 3.10 settings.
16481648
// These are disabled because PEP 637 was rejected.

packages/pyright-internal/src/tests/typeEvaluator4.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ test('Overload7', () => {
311311

312312
test('Overload8', () => {
313313
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['overload8.py']);
314-
TestUtils.validateResults(analysisResults, 2);
314+
TestUtils.validateResults(analysisResults, 4);
315315
});
316316

317317
test('Overload10', () => {
@@ -326,7 +326,7 @@ test('Overload11', () => {
326326

327327
test('Overload12', () => {
328328
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['overload12.py']);
329-
TestUtils.validateResults(analysisResults, 1);
329+
TestUtils.validateResults(analysisResults, 2);
330330
});
331331

332332
test('Overload13', () => {
@@ -527,7 +527,7 @@ test('MemberAccess18', () => {
527527

528528
test('MemberAccess19', () => {
529529
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['memberAccess19.py']);
530-
TestUtils.validateResults(analysisResults, 5);
530+
TestUtils.validateResults(analysisResults, 10);
531531
});
532532

533533
test('MemberAccess20', () => {

0 commit comments

Comments
 (0)