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

Better fill invalid generic instances #15656

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,16 +1704,45 @@ def fix_instance(

return
# Invalid number of type parameters.
fail(
wrong_type_arg_count(len(t.type.type_vars), str(len(t.args)), t.type.name),
t,
code=codes.TYPE_ARG,
)
n = len(t.type.type_vars)
fail(wrong_type_arg_count(n, str(len(t.args)), t.type.name), t, code=codes.TYPE_ARG)
# Construct the correct number of type arguments, as
# otherwise the type checker may crash as it expects
# things to be right.
t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars)
fix_type_var_tuple_argument(AnyType(TypeOfAny.from_error), t)
# things to be right. Reuse existing args if possible.
# Fill missing ones with Any.
any_type = AnyType(TypeOfAny.from_error)
if t.type.has_type_var_tuple_type:
# If type has a TypeVarTuple, assume that args are missing.
assert t.type.type_var_tuple_prefix is not None
assert t.type.type_var_tuple_suffix is not None
# Add args until the TypeVarTuple. Fill with Any if necessary.
args = [
*t.args[: t.type.type_var_tuple_prefix],
*[any_type] * (t.type.type_var_tuple_prefix - len(t.args)),
]
if t.type.type_var_tuple_suffix == 0:
# If TypeVarTuple is last TypeVar, add *tuple[Any, ...].
# There are already args missing before that.
tvt = t.type.defn.type_vars[t.type.type_var_tuple_prefix]
assert isinstance(tvt, TypeVarTupleType)
args.append(UnpackType(Instance(tvt.tuple_fallback.type, [any_type])))
else:
# There are TypeVars after the TypeVarTuple, fill these first.
# As there are already too few args, the type for TypeVarTuple
# must be *tuple[()].
args.extend(
[
*t.args[t.type.type_var_tuple_prefix :],
*[any_type]
* (
t.type.type_var_tuple_suffix
- max(0, len(t.args) - t.type.type_var_tuple_prefix)
),
]
)
t.args = tuple(args)
else:
t.args = tuple([*t.args[:n], *[any_type] * (n - len(t.args))])
t.invalid = True


Expand Down
8 changes: 4 additions & 4 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -5327,10 +5327,10 @@ x: TD
x1 = TD({'x': []})
y: NM
y1 = NM(x=[])
reveal_type(x) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})"
reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[Any]})"
reveal_type(y) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]"
reveal_type(y1) # N: Revealed type is "Tuple[builtins.list[Any], fallback=__main__.NM]"
reveal_type(x) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[builtins.int]})"
reveal_type(x1) # N: Revealed type is "TypedDict('__main__.TD', {'x': builtins.list[builtins.int]})"
reveal_type(y) # N: Revealed type is "Tuple[builtins.list[builtins.int], fallback=__main__.NM]"
reveal_type(y1) # N: Revealed type is "Tuple[builtins.list[builtins.int], fallback=__main__.NM]"
[builtins fixtures/dict.pyi]
[out]

Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ class E(Generic[S, T]): pass

x: C[object] # E: Value of type variable "T" of "C" cannot be "object" [type-var]
y: D[int] # E: Type argument "int" of "D" must be a subtype of "str" [type-var]
z: D[int, int] # E: "D" expects 1 type argument, but 2 given [type-arg]
z: D[int, int] # E: "D" expects 1 type argument, but 2 given [type-arg] \
# E: Type argument "int" of "D" must be a subtype of "str" [type-var]

def h(a: TT, s: S) -> None:
b: C[TT] # E: Invalid type argument value for "C" [type-var]
Expand Down
22 changes: 22 additions & 0 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,28 @@ f(a) # E: Argument 1 to "f" has incompatible type "A[object, Callable[[], None]]
class A(Generic[S, T]): pass
class B: pass

[case testErrorWrongArgCount]
from typing import List, Dict

a1: List
a2: List[int]
a3: List[int, str] # E: "list" expects 1 type argument, but 2 given

reveal_type(a1) # N: Revealed type is "builtins.list[Any]"
reveal_type(a2) # N: Revealed type is "builtins.list[builtins.int]"
reveal_type(a3) # N: Revealed type is "builtins.list[builtins.int]"

b1: Dict
b2: Dict[int] # E: "dict" expects 2 type arguments, but 1 given
b3: Dict[int, str]
b4: Dict[int, str, bool] # E: "dict" expects 2 type arguments, but 3 given

reveal_type(b1) # N: Revealed type is "builtins.dict[Any, Any]"
reveal_type(b2) # N: Revealed type is "builtins.dict[builtins.int, Any]"
reveal_type(b3) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
reveal_type(b4) # N: Revealed type is "builtins.dict[builtins.int, builtins.str]"
[builtins fixtures/dict.pyi]


-- Overloads + generics
-- --------------------
Expand Down
7 changes: 4 additions & 3 deletions test-data/unit/check-newsemanal.test
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ b = A.B('') # E: Argument 1 to "B" has incompatible type "str"; expected "int"
reveal_type(b) # N: Revealed type is "__main__.A.B"
reveal_type(b.x) # N: Revealed type is "builtins.int"
reveal_type(b.f()) # N: Revealed type is "builtins.str"

[case testNewAnalyzerGenerics]
from typing import TypeVar, Generic

Expand All @@ -553,7 +554,7 @@ c2: C[int, str] # E: "C" expects 1 type argument, but 2 given
c3: C
c = C('') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
reveal_type(c.get()) # N: Revealed type is "builtins.int"
reveal_type(c2) # N: Revealed type is "__main__.C[Any]"
reveal_type(c2) # N: Revealed type is "__main__.C[builtins.int]"
reveal_type(c3) # N: Revealed type is "__main__.C[Any]"
[case testNewAnalyzerGenericsTypeVarForwardRef]
from typing import TypeVar, Generic
Expand Down Expand Up @@ -1817,8 +1818,8 @@ def func(x: List[C[T]]) -> T:
x: A
A = List[C[int, str]] # E: "C" expects 1 type argument, but 2 given

reveal_type(x) # N: Revealed type is "builtins.list[__main__.C[Any]]"
reveal_type(func(x)) # N: Revealed type is "Any"
reveal_type(x) # N: Revealed type is "builtins.list[__main__.C[builtins.int]]"
reveal_type(func(x)) # N: Revealed type is "builtins.int"


[builtins fixtures/list.pyi]
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,8 @@ A = List[int, str] | int # E: "list" expects 1 type argument, but 2 given
B = int | list[int, str] # E: "list" expects 1 type argument, but 2 given
a: A
b: B
reveal_type(a) # N: Revealed type is "Union[builtins.list[Any], builtins.int]"
reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.list[Any]]"
reveal_type(a) # N: Revealed type is "Union[builtins.list[builtins.int], builtins.int]"
reveal_type(b) # N: Revealed type is "Union[builtins.int, builtins.list[builtins.int]]"

[case testValidTypeAliasValues]
from typing import TypeVar, Generic, List
Expand Down
48 changes: 48 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,54 @@ y: Array2[int]
takes_empty_array2(y)
[builtins fixtures/tuple.pyi]

[case testTypeVarTuplePep646WrongArgCount]
from typing import Generic, TypeVar
from typing_extensions import TypeVarTuple, Unpack

T1 = TypeVar("T1")
T2 = TypeVar("T2")
Ts = TypeVarTuple("Ts")

class A(Generic[T1, T2, Unpack[Ts]]): ...

def f1(
a1: A,
a2: A[int], # E: "A" expects 3 type arguments, but 1 given
a3: A[int, int],
a4: A[int, int, int],
) -> None:
reveal_type(a1) # N: Revealed type is "__main__.A[Any, Any, Unpack[builtins.tuple[Any, ...]]]"
reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int, Any, Unpack[builtins.tuple[Any, ...]]]"
reveal_type(a3) # N: Revealed type is "__main__.A[builtins.int, builtins.int]"
reveal_type(a4) # N: Revealed type is "__main__.A[builtins.int, builtins.int, builtins.int]"

class B(Generic[T1, Unpack[Ts], T2]): ...

def f2(
b1: B,
b2: B[int], # E: "B" expects 3 type arguments, but 1 given
b3: B[int, int],
b4: B[int, int, int],
) -> None:
reveal_type(b1) # N: Revealed type is "__main__.B[Any, Unpack[builtins.tuple[Any, ...]], Any]"
reveal_type(b2) # N: Revealed type is "__main__.B[builtins.int, Any]"
reveal_type(b3) # N: Revealed type is "__main__.B[builtins.int, builtins.int]"
reveal_type(b4) # N: Revealed type is "__main__.B[builtins.int, builtins.int, builtins.int]"

class C(Generic[Unpack[Ts], T1, T2]): ...

def f3(
c1: C,
c2: C[int], # E: "C" expects 3 type arguments, but 1 given
c3: C[int, int],
c4: C[int, int, int],
) -> None:
reveal_type(c1) # N: Revealed type is "__main__.C[Unpack[builtins.tuple[Any, ...]], Any, Any]"
reveal_type(c2) # N: Revealed type is "__main__.C[builtins.int, Any]"
reveal_type(c3) # N: Revealed type is "__main__.C[builtins.int, builtins.int]"
reveal_type(c4) # N: Revealed type is "__main__.C[builtins.int, builtins.int, builtins.int]"
[builtins fixtures/tuple.pyi]

[case testTypeVarTuplePep646CallableStarArgs]
from typing import Tuple, Callable
from typing_extensions import Unpack, TypeVarTuple
Expand Down