Skip to content
Draft
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
24 changes: 24 additions & 0 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12287,6 +12287,30 @@ export function createTypeEvaluator(
skipUnknownArgCheck = true;
}

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;
const paramName = argParam.paramName;
if (passedName !== paramName) {
addDiagnostic(
DiagnosticRule.reportPositionalArgumentNameMismatch,
`Positional argument "${passedName}" does not match parameter name "${paramName}"; pass as a keyword argument to avoid confusion`,
a.valueExpression
);
}
}
});

// Run through all args and validate them against their matched parameter.
// We'll do two phases. The first one establishes constraints for type
// variables. The second perform type validation using the solved
Expand Down
9 changes: 9 additions & 0 deletions packages/pyright-internal/src/common/configOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ export interface DiagnosticRuleSet {
// Report issues related to call expressions?
reportCallIssue: DiagnosticLevel;

// Report mismatch between positional argument names and parameter names when requested by decorator?
reportPositionalArgumentNameMismatch: DiagnosticLevel;

// Report inconsistencies with function overload signatures?
reportInconsistentOverload: DiagnosticLevel;

Expand Down Expand Up @@ -621,6 +624,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportAssignmentType: 'none',
reportAttributeAccessIssue: 'none',
reportCallIssue: 'none',
reportPositionalArgumentNameMismatch: 'none',
reportInconsistentOverload: 'none',
reportIndexIssue: 'none',
reportInvalidTypeArguments: 'none',
Expand Down Expand Up @@ -740,6 +744,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportPositionalArgumentNameMismatch: 'none',
reportInconsistentOverload: 'error',
reportIndexIssue: 'error',
reportInvalidTypeArguments: 'error',
Expand Down Expand Up @@ -859,6 +864,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet {
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportPositionalArgumentNameMismatch: 'none',
reportInconsistentOverload: 'error',
reportIndexIssue: 'error',
reportInvalidTypeArguments: 'error',
Expand Down Expand Up @@ -1053,6 +1059,7 @@ export const getRecommendedDiagnosticRuleSet = (): DiagnosticRuleSet => ({
reportUnannotatedClassAttribute: 'warning',
reportIncompatibleUnannotatedOverride: 'none', // TODO: change to error when we're confident there's no performance issues with this rule
reportInvalidAbstractMethod: 'warning',
reportPositionalArgumentNameMismatch: 'warning',
allowedUntypedLibraries: [],
});

Expand Down Expand Up @@ -1168,6 +1175,7 @@ export const getAllDiagnosticRuleSet = (): DiagnosticRuleSet => ({
reportUnannotatedClassAttribute: 'error',
reportIncompatibleUnannotatedOverride: 'error',
reportInvalidAbstractMethod: 'error',
reportPositionalArgumentNameMismatch: 'error',
allowedUntypedLibraries: [],
});

Expand Down Expand Up @@ -1208,6 +1216,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportPositionalArgumentNameMismatch: 'warning',
reportInconsistentOverload: 'error',
reportIndexIssue: 'error',
reportInvalidTypeArguments: 'error',
Expand Down
1 change: 1 addition & 0 deletions packages/pyright-internal/src/common/diagnosticRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export enum DiagnosticRule {
reportUnreachable = 'reportUnreachable',
reportShadowedImports = 'reportShadowedImports',
reportImplicitOverride = 'reportImplicitOverride',
reportPositionalArgumentNameMismatch = 'reportPositionalArgumentNameMismatch',

// basedpyright options:
failOnWarnings = 'failOnWarnings',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ConfigOptions } from '../common/configOptions';
import { Uri } from '../common/uri/uri';
import { DiagnosticRule } from '../common/diagnosticRules';
import * as TestUtils from './testUtils';

test('it reports an error', () => {
const configOptions = new ConfigOptions(Uri.empty());
configOptions.diagnosticRuleSet.reportUnnecessaryTypeIgnoreComment = 'error';
configOptions.diagnosticRuleSet.reportPositionalArgumentNameMismatch = 'warning';
configOptions.diagnosticRuleSet.reportUnusedParameter = 'none';

const analysisResults = TestUtils.typeAnalyzeSampleFiles(['argumentsMatchParamNames.py'], configOptions);

TestUtils.validateResultsButBased(analysisResults, {
warnings: [],
errors: [],
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import overload

def foo(
height: int,
width: int,
): ...


height = 1
length = 2

foo(
height, # okay, name matches parameter name
length, # pyright: ignore[reportPositionalArgumentNameMismatch]
)

foo(
height=height, # okay, using keyword
width=length, # okay, using keyword
)

foo(
height, # okay, matches
width=length, # okay, using keyword
)

def bar(a: int, /, b: int): ...

bar(
1,
2,
)

bar(
1,
b=2, # pyright: ignore[reportPositionalArgumentNameMismatch]
)

len([])

@overload
def f(a: int, b: int): ...
@overload
def f(a: str, b: str): ...

def f(a, b): ...

f(
1, # pyright: ignore[reportPositionalArgumentNameMismatch]
2, # pyright: ignore[reportPositionalArgumentNameMismatch]
)
Loading