Fixed a bug in negative type narrowing logic for value patterns in match statement.

This commit is contained in:
Eric Traut 2022-02-16 22:08:17 -08:00
parent 9b0ea9eb69
commit ef72e4079c
2 changed files with 32 additions and 19 deletions

View File

@ -734,44 +734,43 @@ function narrowTypeBasedOnValuePattern(
evaluator.mapSubtypesExpandTypeVars(
subjectType,
getTypeCondition(valueSubtypeExpanded),
(_, subjectSubtypeUnexpanded) => {
(subjectSubtypeExpanded) => {
// If this is a negative test, see if it's an enum value.
if (!isPositiveTest) {
if (
isClassInstance(subjectSubtypeUnexpanded) &&
ClassType.isEnumClass(subjectSubtypeUnexpanded) &&
!isLiteralType(subjectSubtypeUnexpanded) &&
isClassInstance(valueSubtypeUnexpanded) &&
isSameWithoutLiteralValue(subjectSubtypeUnexpanded, valueSubtypeUnexpanded) &&
isLiteralType(valueSubtypeUnexpanded)
isClassInstance(subjectSubtypeExpanded) &&
ClassType.isEnumClass(subjectSubtypeExpanded) &&
!isLiteralType(subjectSubtypeExpanded) &&
isClassInstance(valueSubtypeExpanded) &&
isSameWithoutLiteralValue(subjectSubtypeExpanded, valueSubtypeExpanded) &&
isLiteralType(valueSubtypeExpanded)
) {
const allEnumTypes = enumerateLiteralsForType(evaluator, subjectSubtypeUnexpanded);
const allEnumTypes = enumerateLiteralsForType(evaluator, subjectSubtypeExpanded);
if (allEnumTypes) {
return combineTypes(
allEnumTypes.filter(
(enumType) =>
!ClassType.isLiteralValueSame(valueSubtypeUnexpanded, enumType)
(enumType) => !ClassType.isLiteralValueSame(valueSubtypeExpanded, enumType)
)
);
}
} else if (
isClassInstance(subjectSubtypeUnexpanded) &&
isClassInstance(valueSubtypeUnexpanded) &&
ClassType.isLiteralValueSame(valueSubtypeUnexpanded, subjectSubtypeUnexpanded)
isClassInstance(subjectSubtypeExpanded) &&
isClassInstance(valueSubtypeExpanded) &&
ClassType.isLiteralValueSame(valueSubtypeExpanded, subjectSubtypeExpanded)
) {
return undefined;
}
return subjectSubtypeUnexpanded;
return subjectSubtypeExpanded;
}
if (isNever(valueSubtypeExpanded) || isNever(subjectSubtypeUnexpanded)) {
if (isNever(valueSubtypeExpanded) || isNever(subjectSubtypeExpanded)) {
return NeverType.createNever();
}
if (isAnyOrUnknown(valueSubtypeExpanded) || isAnyOrUnknown(subjectSubtypeUnexpanded)) {
if (isAnyOrUnknown(valueSubtypeExpanded) || isAnyOrUnknown(subjectSubtypeExpanded)) {
// If either type is "Unknown" (versus Any), propagate the Unknown.
return isUnknown(valueSubtypeExpanded) || isUnknown(subjectSubtypeUnexpanded)
return isUnknown(valueSubtypeExpanded) || isUnknown(subjectSubtypeExpanded)
? UnknownType.create()
: AnyType.create();
}
@ -781,7 +780,7 @@ function narrowTypeBasedOnValuePattern(
const returnType = evaluator.useSpeculativeMode(pattern.expression, () =>
evaluator.getTypeFromMagicMethodReturn(
valueSubtypeExpanded,
[subjectSubtypeUnexpanded],
[subjectSubtypeExpanded],
'__eq__',
pattern.expression,
/* expectedType */ undefined

View File

@ -1,7 +1,7 @@
# This sample tests type checking for match statements (as
# described in PEP 634) that contain value patterns.
from enum import Enum
from enum import Enum, auto
from typing import Tuple, TypeVar, Union
from http import HTTPStatus
@ -84,3 +84,17 @@ def test_enum_narrowing(m: Union[Medal, Color, int]):
case d1:
reveal_type(d1, expected_text='int | Literal[Medal.bronze]')
reveal_type(m, expected_text='int | Literal[Medal.bronze]')
class Foo(Enum):
bar = auto()
def __str__(self) -> str:
match self:
case Foo.bar:
return "bar"
case x:
reveal_type(x, expected_text="Never")