Skip to content

Commit

Permalink
Fixed recent regression that results in incorrect narrowed type when …
Browse files Browse the repository at this point in the history
…using the `x in y` pattern where `y` is a container type whose element type is `Unknown` or `Any`. This addresses #9457. (#9469)
  • Loading branch information
erictraut authored Nov 16, 2024
1 parent 6adc4cf commit ce1325f
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 32 deletions.
2 changes: 1 addition & 1 deletion packages/pyright-internal/src/analyzer/typeGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2060,7 +2060,7 @@ export function getElementTypeForContainerNarrowing(containerType: Type) {
export function narrowTypeForContainerElementType(evaluator: TypeEvaluator, referenceType: Type, elementType: Type) {
return evaluator.mapSubtypesExpandTypeVars(referenceType, /* options */ undefined, (referenceSubtype) => {
return mapSubtypes(elementType, (elementSubtype) => {
if (isAnyOrUnknown(referenceSubtype)) {
if (isAnyOrUnknown(elementSubtype)) {
return referenceSubtype;
}

Expand Down
52 changes: 22 additions & 30 deletions packages/pyright-internal/src/tests/samples/typeNarrowingIn1.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
# This sample tests type narrowing for the "in" operator.

from typing import Callable, Generic, Literal, ParamSpec, TypeVar, TypedDict
from typing import Any, Callable, Generic, Literal, ParamSpec, TypeVar, TypedDict
import random


def verify_str(p: str) -> None: ...


def verify_int(p: int) -> None: ...


def verify_none(p: None) -> None: ...


x: str | None
y: int | str
if random.random() < 0.5:
x = None
y = 1
else:
x = "2"
y = "2"

if x in ["2"]:
verify_str(x)

# This should generate an error because x should
# be narrowed to a str.
verify_none(x)
def func0(x: str | None, y: int | str):
if random.random() < 0.5:
x = None
y = 1
else:
x = "2"
y = "2"

if y in [2]:
verify_int(y)
if x in ["2"]:
reveal_type(x, expected_text="Literal['2']")

# This should generate an error because y should
# be narrowed to an int.
verify_str(y)
if y in [1]:
reveal_type(y, expected_text="Literal[1]")


def func1(x: int | str | None, y: Literal[1, 2, "b"], b: int):
Expand Down Expand Up @@ -184,3 +166,13 @@ def func13(x: type[T13]) -> type[T13]:
reveal_type(x, expected_text="type[str]* | type[int]* | type[float]*")

return x


def func14(x: str, y: dict[Any, Any]):
if x in y:
reveal_type(x, expected_text="str")


def func15(x: Any, y: dict[str, str]):
if x in y:
reveal_type(x, expected_text="str")
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 @@ -496,7 +496,7 @@ test('TypeNarrowingTupleLength1', () => {
test('TypeNarrowingIn1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeNarrowingIn1.py']);

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

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

0 comments on commit ce1325f

Please sign in to comment.