diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index cb74bbcf3a..243f1a8088 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -12287,24 +12287,39 @@ export function createTypeEvaluator( skipUnknownArgCheck = true; } + const paramDetails = getParamListDetails(type); matchResults.argParams.forEach((argParam) => { const a = argParam.argument; if ( a.argCategory === ArgCategory.Simple && !a.name && a.valueExpression && - a.valueExpression.nodeType === ParseNodeType.Name && argParam.paramCategory === ParamCategory.Simple && !argParam.mapsToVarArgList && argParam.paramName && !argParam.isParamNameSynthesized ) { - const passedName = (a.valueExpression as NameNode).d.value; + // Skip positional-only parameters + const paramIndex = paramDetails.params.findIndex((p) => p.param.name === argParam.paramName); + if (paramIndex >= 0 && paramIndex < paramDetails.positionOnlyParamCount) { + return; + } + const paramName = argParam.paramName; - if (passedName !== paramName) { + if (a.valueExpression.nodeType === ParseNodeType.Name) { + const passedName = (a.valueExpression as NameNode).d.value; + if (passedName !== paramName) { + addDiagnostic( + DiagnosticRule.reportPositionalArgumentNameMismatch, + `Argument "${passedName}" does not match parameter name "${paramName}" for function "${type.shared.name}"; pass as a keyword argument to avoid confusion`, + a.valueExpression + ); + } + } else { + // Non-name expression passed positionally addDiagnostic( DiagnosticRule.reportPositionalArgumentNameMismatch, - `Positional argument "${passedName}" does not match parameter name "${paramName}"; pass as a keyword argument to avoid confusion`, + `Positional argument for parameter "${paramName}" in function "${type.shared.name}"; pass as a keyword argument to avoid confusion`, a.valueExpression ); } diff --git a/packages/pyright-internal/src/tests/argumentsMatchParamNames.test.ts b/packages/pyright-internal/src/tests/argumentsMatchParamNames.test.ts index 632875e611..25f933d99e 100644 --- a/packages/pyright-internal/src/tests/argumentsMatchParamNames.test.ts +++ b/packages/pyright-internal/src/tests/argumentsMatchParamNames.test.ts @@ -16,6 +16,18 @@ test('it reports an error', () => { line: 11, code: DiagnosticRule.reportPositionalArgumentNameMismatch, }, + { + line: 25, + code: DiagnosticRule.reportPositionalArgumentNameMismatch, + }, + { + line: 32, + code: DiagnosticRule.reportPositionalArgumentNameMismatch, + }, + { + line: 33, + code: DiagnosticRule.reportPositionalArgumentNameMismatch, + }, ], }); }); diff --git a/packages/pyright-internal/src/tests/samples/argumentsMatchParamNames.py b/packages/pyright-internal/src/tests/samples/argumentsMatchParamNames.py index a1cc5c0c3e..e4794cff3e 100644 --- a/packages/pyright-internal/src/tests/samples/argumentsMatchParamNames.py +++ b/packages/pyright-internal/src/tests/samples/argumentsMatchParamNames.py @@ -21,3 +21,15 @@ def foo( height, # okay, matches width=length, # okay, using keyword ) + +foo( + 1, # should display a warning, positional argument used + width=2, # okay, using keyword +) + +len([]) # okay, built-in function with no keyword arguments + +pow( + 2, # should display a warning, positional argument used, built-in function + 3, # should display a warning, positional argument used, built-in function +)