Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when expanding invalid Unpack in a Callable alias #17028

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 17 additions & 14 deletions mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
Type,
TypeAliasType,
TypedDictType,
TypeOfAny,
TypeType,
TypeVarId,
TypeVarLikeType,
Expand Down Expand Up @@ -312,24 +313,26 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l
suffix = self.expand_types(t.arg_types[star_index + 1 :])

var_arg_type = get_proper_type(var_arg.type)
new_unpack: Type
if isinstance(var_arg_type, Instance):
# we have something like Unpack[Tuple[Any, ...]]
new_unpack = var_arg
else:
if isinstance(var_arg_type, TupleType):
# We have something like Unpack[Tuple[Unpack[Ts], X1, X2]]
expanded_tuple = var_arg_type.accept(self)
assert isinstance(expanded_tuple, ProperType) and isinstance(
expanded_tuple, TupleType
)
expanded_items = expanded_tuple.items
fallback = var_arg_type.partial_fallback
else:
# We have plain Unpack[Ts]
assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type)
fallback = var_arg_type.tuple_fallback
expanded_items = self.expand_unpack(var_arg)
elif isinstance(var_arg_type, TupleType):
# We have something like Unpack[Tuple[Unpack[Ts], X1, X2]]
expanded_tuple = var_arg_type.accept(self)
assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType)
expanded_items = expanded_tuple.items
fallback = var_arg_type.partial_fallback
new_unpack = UnpackType(TupleType(expanded_items, fallback))
elif isinstance(var_arg_type, TypeVarTupleType):
# We have plain Unpack[Ts]
fallback = var_arg_type.tuple_fallback
expanded_items = self.expand_unpack(var_arg)
new_unpack = UnpackType(TupleType(expanded_items, fallback))
else:
# We have invalid type in Unpack. This can happen when expanding aliases
# to Callable[[*Invalid], Ret]
new_unpack = AnyType(TypeOfAny.from_error, line=var_arg.line, column=var_arg.column)
return prefix + [new_unpack] + suffix

def visit_callable_type(self, t: CallableType) -> CallableType:
Expand Down
31 changes: 31 additions & 0 deletions test-data/unit/check-python311.test
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,34 @@ myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" m
# N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2]
reveal_type(myclass3) # N: Revealed type is "Any"
[builtins fixtures/tuple.pyi]

[case testUnpackNewSyntaxInvalidCallableAlias]
from typing import Any, Callable, List, Tuple, TypeVar, Unpack

T = TypeVar("T")
Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined

def good(*x: int) -> int: ...
def bad(*x: int, y: int) -> int: ...

Alias1 = Callable[[*Ts], int] # E: Variable "__main__.Ts" is not valid as a type \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
x1: Alias1[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(x1) # N: Revealed type is "def (*Any) -> builtins.int"
x1 = good
x1 = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]")

Alias2 = Callable[[*T], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple)
x2: Alias2[int]
reveal_type(x2) # N: Revealed type is "def (*Any) -> builtins.int"

Unknown = Any
Alias3 = Callable[[*Unknown], int]
x3: Alias3[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int"

IntList = List[int]
Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple)
x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int"
[builtins fixtures/tuple.pyi]
7 changes: 3 additions & 4 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,9 @@ BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is no
ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]"

# TODO this should report errors on the two following lines
#BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str])
#ba2: BadAlias2[int]
#reveal_type(ba2)
BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) # E: TypeVarTuple "Ts" is not included in type_params
ba2: BadAlias2[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str"

[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]
7 changes: 3 additions & 4 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -1185,10 +1185,9 @@ Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included i
unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2
reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any"

# TODO this should report errors on the two following lines
#Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str])
#unbound_tvt_alias2: Ta10[int]
#reveal_type(unbound_tvt_alias2)
Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) # E: TypeVarTuple "Ts" is not included in type_params
unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str"

[builtins fixtures/dict.pyi]

Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -2278,6 +2278,22 @@ higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Cal
higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]"
[builtins fixtures/tuple.pyi]

[case testAliasToCallableWithUnpackInvalid]
from typing import Any, Callable, List, Tuple, TypeVar, Unpack

T = TypeVar("T")
Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined

def good(*x: int) -> int: ...
def bad(*x: int, y: int) -> int: ...

Alias = Callable[[Unpack[T]], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple)
x: Alias[int]
reveal_type(x) # N: Revealed type is "def (*Any) -> builtins.int"
x = good
x = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]")
[builtins fixtures/tuple.pyi]

[case testTypeVarTupleInvariant]
from typing import Generic, Tuple
from typing_extensions import Unpack, TypeVarTuple
Expand Down