Fixed a bug that results in incorrect (local) type evaluation of an instance variable with a declared type when assigned within a loop that uses an augmented assignment. This addresses #8090. (#8093)

This commit is contained in:
Eric Traut 2024-06-07 15:51:02 -07:00 committed by GitHub
parent aad860332e
commit ec229aa925
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 8 deletions

View File

@ -117,6 +117,7 @@ export function validateBinaryOperation(
): Type {
const leftType = leftTypeResult.type;
const rightType = rightTypeResult.type;
const isIncomplete = !!leftTypeResult.isIncomplete || !!rightTypeResult.isIncomplete;
let type: Type | undefined;
let concreteLeftType = evaluator.makeTopLevelTypeVarsConcrete(leftType);
@ -485,7 +486,8 @@ export function validateBinaryOperation(
);
}
}
return resultType;
return resultType ?? UnknownType.create(isIncomplete);
}
);
}
@ -493,7 +495,7 @@ export function validateBinaryOperation(
}
}
return type ?? UnknownType.create();
return type ?? UnknownType.create(isIncomplete);
}
export function getTypeOfBinaryOperation(
@ -738,7 +740,7 @@ export function getTypeOfBinaryOperation(
// within a loop construct using __add__.
const isTupleAddAllowed = !isUnion(leftType);
let type = validateBinaryOperation(
const type = validateBinaryOperation(
evaluator,
node.operator,
{ type: leftType, isIncomplete: leftTypeResult.isIncomplete },
@ -788,8 +790,6 @@ export function getTypeOfBinaryOperation(
);
}
}
type = UnknownType.create();
}
return { type, isIncomplete, typeErrors };
@ -934,8 +934,6 @@ export function getTypeOfAugmentedAssignment(
node
);
}
type = UnknownType.create();
}
typeResult = { type, isIncomplete };
@ -1073,7 +1071,7 @@ export function getTypeOfUnaryOperation(
}
}
type = UnknownType.create();
type = UnknownType.create(isIncomplete);
}
}
}

View File

@ -26040,6 +26040,11 @@ export function createTypeEvaluator(
// should be completely reworked once there has been a public discussion
// about the correct behavior.
// If the result is incomplete, do not attempt to narrow the type.
if (assignedTypeResult.isIncomplete) {
return assignedTypeResult;
}
const narrowedType = mapSubtypes(assignedTypeResult.type, (assignedSubtype) => {
// Handle the special case where the assigned type is a literal type.
// Some types include very large unions of literal types, and we don't

View File

@ -0,0 +1,24 @@
# This sample tests a case where an instance variable is assigned within
# a loop using its own value.
# pyright: strict
class ClassA:
x: int | None
def method1(self) -> None:
self.x = 0
for _ in range(1, 10):
self.x = reveal_type(self.x, expected_text="int") + 1
reveal_type(self.x, expected_text="int")
def method2(self) -> None:
self.x = 0
for _ in range(1, 10):
self.x += 1
reveal_type(self.x, expected_text="int")

View File

@ -455,6 +455,12 @@ test('Loop47', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('Loop48', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['loop48.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('ForLoop1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['forLoop1.py']);