Skip to content

Commit

Permalink
Fixed a bug that results in a false negative when a None type is in…
Browse files Browse the repository at this point in the history
…cluded in an unpacked argument within a function call. This addresses #7175.
  • Loading branch information
erictraut committed Feb 1, 2024
1 parent a81633d commit 0d75f62
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 23 deletions.
22 changes: 11 additions & 11 deletions packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2695,8 +2695,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
let type = transformPossibleRecursiveTypeAlias(typeResult.type);
type = makeTopLevelTypeVarsConcrete(type);

if (isOptionalType(type)) {
if (!typeResult.isIncomplete && emitNotIterableError) {
if (isOptionalType(type) && emitNotIterableError) {
if (!typeResult.isIncomplete) {
addDiagnostic(DiagnosticRule.reportOptionalIterable, LocMessage.noneNotIterable(), errorNode);
}
type = removeNoneFromUnion(type);
Expand Down Expand Up @@ -10205,13 +10205,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
} else if (isParamSpec(argType) && argType.paramSpecAccess === 'args') {
listElementType = undefined;
} else {
listElementType =
getTypeOfIterator(
{ type: argType, isIncomplete: argTypeResult.isIncomplete },
/* isAsync */ false,
errorNode,
/* emitNotIterableError */ false
)?.type ?? UnknownType.create();
listElementType = getTypeOfIterator(
{ type: argType, isIncomplete: argTypeResult.isIncomplete },
/* isAsync */ false,
errorNode,
/* emitNotIterableError */ false
)?.type;

if (paramDetails.params[paramIndex].param.category !== ParameterCategory.ArgsList) {
matchedUnpackedListOfUnknownLength = true;
Expand All @@ -10223,8 +10222,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
argumentCategory: ArgumentCategory.Simple,
typeResult: { type: listElementType, isIncomplete: argTypeResult.isIncomplete },
}
: undefined;
if (funcArg && argTypeResult.isIncomplete) {
: { ...argList[argIndex] };

if (argTypeResult.isIncomplete) {
isTypeIncomplete = true;
}

Expand Down
35 changes: 24 additions & 11 deletions packages/pyright-internal/src/tests/samples/call6.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,48 @@
# tuples and *args parameters.


def foo1(a: int, b: int):
def func1(a: int, b: int):
pass


def foo2(*args: int):
def func2(*args: int):
pass


fixed_tuple_0 = ()
foo1(*fixed_tuple_0, 2, 3)
foo2(*fixed_tuple_0, 2)
func1(*fixed_tuple_0, 2, 3)
func2(*fixed_tuple_0, 2)

fixed_tuple_1 = (1,)

# This should generate an error because there
# are too many parameters.
foo1(*fixed_tuple_1, 2, 3)
func1(*fixed_tuple_1, 2, 3)

foo2(*fixed_tuple_1, 2, *fixed_tuple_0)
func2(*fixed_tuple_1, 2, *fixed_tuple_0)

fixed_tuple_3 = (1, 3, 5)

# This should generate an error because there
# are too many parameters.
foo1(*fixed_tuple_3, 2)
func1(*fixed_tuple_3, 2)

foo2(*fixed_tuple_3, 2, *fixed_tuple_0)
func2(*fixed_tuple_3, 2, *fixed_tuple_0)

homogen_tuple: tuple[int, ...] = (1, 5, 3)
unbounded_tuple: tuple[int, ...] = (1, 5, 3)

foo2(*homogen_tuple)
foo2(*homogen_tuple, 2)
func2(*unbounded_tuple)
func2(*unbounded_tuple, 2)


def func3(*args: str): ...


def func4(v1: list[str] | None, v2: None, v3: list[str]):
# This should generate an error.
func3(*v1)

# This should generate an error.
func3(*v2)

func3(*v3)
2 changes: 1 addition & 1 deletion packages/pyright-internal/src/tests/typeEvaluator1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ test('Call5', () => {
test('Call6', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['call6.py']);

TestUtils.validateResults(analysisResults, 2);
TestUtils.validateResults(analysisResults, 4);
});

test('Call7', () => {
Expand Down

0 comments on commit 0d75f62

Please sign in to comment.