Skip to content

Commit

Permalink
Fix crash when expanding invalid Unpack in a Callable alias (#17028)
Browse files Browse the repository at this point in the history
Fixes #16937
  • Loading branch information
hamdanal authored Apr 8, 2024
1 parent 3ff6e47 commit e2fc1f2
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 22 deletions.
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 @@ -1190,10 +1190,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"

class A(Generic[T]):
Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \
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

0 comments on commit e2fc1f2

Please sign in to comment.