Skip to content

Commit 310c75d

Browse files
committed
Address part of CR
1 parent 2f30c87 commit 310c75d

File tree

3 files changed

+63
-22
lines changed

3 files changed

+63
-22
lines changed

mypy/semanal.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,7 @@ def analyze_unbound_tvar_impl(
20222022
self, t: UnboundType, is_unpacked: bool = False, is_typealias_param: bool = False
20232023
) -> tuple[str, TypeVarLikeExpr] | None:
20242024
if is_unpacked and is_typealias_param:
2025-
return None # This should be unreachable
2025+
assert False, "Unreachable"
20262026
sym = self.lookup_qualified(t.name, t)
20272027
if sym and isinstance(sym.node, PlaceholderNode):
20282028
self.record_incomplete_ref()
@@ -3538,6 +3538,7 @@ def analyze_alias(
35383538
in_dynamic_func=dynamic,
35393539
global_scope=global_scope,
35403540
allowed_alias_tvars=tvar_defs,
3541+
has_type_params=declared_type_vars is not None,
35413542
)
35423543

35433544
# There can be only one variadic variable at most, the error is reported elsewhere.
@@ -3588,9 +3589,11 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
35883589
type_params: TypeVarLikeList | None
35893590
if self.check_type_alias_type_call(s.rvalue, name=lvalue.name):
35903591
rvalue = s.rvalue.args[1]
3592+
pep_695 = True
35913593
type_params = self.analyze_type_alias_type_params(s.rvalue)
35923594
else:
35933595
rvalue = s.rvalue
3596+
pep_695 = False
35943597
type_params = None
35953598

35963599
if isinstance(rvalue, CallExpr) and rvalue.analyzed:
@@ -3631,7 +3634,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
36313634
# without this rule, this typical use case will require a lot of explicit
36323635
# annotations (see the second rule).
36333636
return False
3634-
if not pep_613 and not self.can_be_type_alias(rvalue):
3637+
if not pep_613 and not pep_695 and not self.can_be_type_alias(rvalue):
36353638
return False
36363639

36373640
if existing and not isinstance(existing.node, (PlaceholderNode, TypeAlias)):
@@ -3686,7 +3689,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
36863689
and isinstance(res, Instance)
36873690
and not res.args
36883691
and not empty_tuple_index
3689-
and type_params is None
3692+
and not pep_695
36903693
)
36913694
if isinstance(res, ProperType) and isinstance(res, Instance):
36923695
if not validate_instance(res, self.fail, empty_tuple_index):

mypy/typeanal.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def analyze_type_alias(
136136
in_dynamic_func: bool = False,
137137
global_scope: bool = True,
138138
allowed_alias_tvars: list[TypeVarLikeType] | None = None,
139+
has_type_params: bool = False,
139140
) -> tuple[Type, set[str]]:
140141
"""Analyze r.h.s. of a (potential) type alias definition.
141142
@@ -153,6 +154,7 @@ def analyze_type_alias(
153154
allow_placeholder=allow_placeholder,
154155
prohibit_self_type="type alias target",
155156
allowed_alias_tvars=allowed_alias_tvars,
157+
has_type_params=has_type_params,
156158
)
157159
analyzer.in_dynamic_func = in_dynamic_func
158160
analyzer.global_scope = global_scope
@@ -205,6 +207,7 @@ def __init__(
205207
prohibit_self_type: str | None = None,
206208
allowed_alias_tvars: list[TypeVarLikeType] | None = None,
207209
allow_type_any: bool = False,
210+
has_type_params: bool = False,
208211
) -> None:
209212
self.api = api
210213
self.fail_func = api.fail
@@ -226,6 +229,7 @@ def __init__(
226229
if allowed_alias_tvars is None:
227230
allowed_alias_tvars = []
228231
self.allowed_alias_tvars = allowed_alias_tvars
232+
self.has_type_params = has_type_params
229233
# If false, record incomplete ref if we generate PlaceholderType.
230234
self.allow_placeholder = allow_placeholder
231235
# Are we in a context where Required[] is allowed?
@@ -320,7 +324,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
320324
if tvar_def is None:
321325
if self.allow_unbound_tvars:
322326
return t
323-
self.fail(f'ParamSpec "{t.name}" is unbound', t, code=codes.VALID_TYPE)
327+
if self.has_type_params:
328+
msg = f'ParamSpec "{t.name}" is not included in type_params'
329+
else:
330+
msg = f'ParamSpec "{t.name}" is unbound'
331+
self.fail(msg, t, code=codes.VALID_TYPE)
324332
return AnyType(TypeOfAny.from_error)
325333
assert isinstance(tvar_def, ParamSpecType)
326334
if len(t.args) > 0:
@@ -344,11 +352,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
344352
and not defining_literal
345353
and (tvar_def is None or tvar_def not in self.allowed_alias_tvars)
346354
):
347-
self.fail(
348-
f'Can\'t use bound type variable "{t.name}" to define generic alias',
349-
t,
350-
code=codes.VALID_TYPE,
351-
)
355+
if self.has_type_params:
356+
msg = f'Type variable "{t.name}" is not included in type_params'
357+
else:
358+
msg = f'Can\'t use bound type variable "{t.name}" to define generic alias'
359+
self.fail(msg, t, code=codes.VALID_TYPE)
352360
return AnyType(TypeOfAny.from_error)
353361
if isinstance(sym.node, TypeVarExpr) and tvar_def is not None:
354362
assert isinstance(tvar_def, TypeVarType)
@@ -363,17 +371,21 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
363371
and self.defining_alias
364372
and tvar_def not in self.allowed_alias_tvars
365373
):
366-
self.fail(
367-
f'Can\'t use bound type variable "{t.name}" to define generic alias',
368-
t,
369-
code=codes.VALID_TYPE,
370-
)
374+
if self.has_type_params:
375+
msg = f'Type variable "{t.name}" is not included in type_params'
376+
else:
377+
msg = f'Can\'t use bound type variable "{t.name}" to define generic alias'
378+
self.fail(msg, t, code=codes.VALID_TYPE)
371379
return AnyType(TypeOfAny.from_error)
372380
if isinstance(sym.node, TypeVarTupleExpr):
373381
if tvar_def is None:
374382
if self.allow_unbound_tvars:
375383
return t
376-
self.fail(f'TypeVarTuple "{t.name}" is unbound', t, code=codes.VALID_TYPE)
384+
if self.has_type_params:
385+
msg = f'TypeVarTuple "{t.name}" is not included in type_params'
386+
else:
387+
msg = f'TypeVarTuple "{t.name}" is unbound'
388+
self.fail(msg, t, code=codes.VALID_TYPE)
377389
return AnyType(TypeOfAny.from_error)
378390
assert isinstance(tvar_def, TypeVarTupleType)
379391
if not self.allow_type_var_tuple:

test-data/unit/check-type-aliases.test

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,17 +1085,18 @@ T1 = T2 = TypeAliasType("T", int)
10851085
t1: T1 # E: Variable "__main__.T1" is not valid as a type \
10861086
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
10871087

1088-
T3 = TypeAliasType("T3", -1)
1089-
t3: T3 # E: Variable "__main__.T3" is not valid as a type \
1090-
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
1088+
T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead?
1089+
t3: T3
1090+
reveal_type(t3) # N: Revealed type is "Any"
10911091
[builtins fixtures/tuple.pyi]
10921092

10931093
[case testTypeAliasTypeGeneric]
1094-
from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec
1095-
from typing import Callable, Dict, TypeVar, Tuple, Unpack
1094+
from typing import Callable, Dict, Generic, TypeVar, Tuple
1095+
from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec, Unpack
10961096

10971097
K = TypeVar('K')
10981098
V = TypeVar('V')
1099+
T = TypeVar('T')
10991100
Ts = TypeVarTuple("Ts")
11001101
Ts1 = TypeVarTuple("Ts1")
11011102
P = ParamSpec("P")
@@ -1123,14 +1124,20 @@ def g(x: int, y: float) -> int: return 1
11231124
xp1: ParamAlias[str, float] = f
11241125
xp2: ParamAlias[str, float] = g # E: Incompatible types in assignment (expression has type "Callable[[int, float], int]", variable has type "Callable[[str, float], int]")
11251126
xp3: ParamAlias[str, float] = lambda x, y: 1
1127+
1128+
class G(Generic[P, T]): ...
1129+
ParamAlias2 = TypeAliasType("ParamAlias2", G[P, T], type_params=(P, T))
1130+
xp: ParamAlias2[[int], str]
1131+
reveal_type(xp) # N: Revealed type is "__main__.G[[builtins.int], builtins.str]"
11261132
[builtins fixtures/dict.pyi]
11271133

11281134
[case testTypeAliasTypeInvalidGeneric]
11291135
from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec
1130-
from typing import Dict, TypeVar, Tuple, Unpack
1136+
from typing import Callable, Dict, Generic, TypeVar, Tuple, Unpack
11311137

11321138
K = TypeVar('K')
11331139
V = TypeVar('V')
1140+
T = TypeVar('T')
11341141
Ts = TypeVarTuple("Ts")
11351142
Ts1 = TypeVarTuple("Ts1")
11361143
P = ParamSpec("P")
@@ -1139,7 +1146,7 @@ Ta1 = TypeAliasType("Ta1", int, type_params=K) # E: Tuple literal expected as t
11391146

11401147
Ta2 = TypeAliasType("Ta2", int, type_params=(None,)) # E: Free type variable expected in type_params argument to TypeAliasType
11411148

1142-
Ta3 = TypeAliasType("Ta3", Dict[K, V], type_params=(V,)) # E: Can't use bound type variable "K" to define generic alias
1149+
Ta3 = TypeAliasType("Ta3", Dict[K, V], type_params=(V,)) # E: Type variable "K" is not included in type_params
11431150
partially_generic1: Ta3[int] = {"a": 1}
11441151
reveal_type(partially_generic1) # N: Revealed type is "builtins.dict[Any, builtins.int]"
11451152
partially_generic2: Ta3[int] = {1: "a"} # E: Dict entry 0 has incompatible type "int": "str"; expected "Any": "int"
@@ -1148,6 +1155,25 @@ Ta4 = TypeAliasType("Ta4", Tuple[Unpack[Ts]], type_params=(Ts, Ts1)) # E: Can o
11481155

11491156
Ta5 = TypeAliasType("Ta5", Dict) # Unlike old style aliases, this is not generic
11501157
non_generic_dict: Ta5[int, str] # E: Bad number of arguments for type alias, expected 0, given 2
1158+
reveal_type(non_generic_dict) # N: Revealed type is "builtins.dict[Any, Any]"
1159+
1160+
Ta6 = TypeAliasType("Ta6", Tuple[Unpack[Ts]]) # E: TypeVarTuple "Ts" is not included in type_params
1161+
unbound_tvt_alias: Ta6[int] # E: Bad number of arguments for type alias, expected 0, given 1
1162+
reveal_type(unbound_tvt_alias) # N: Revealed type is "builtins.tuple[Any, ...]"
1163+
1164+
class G(Generic[P, T]): ...
1165+
Ta7 = TypeAliasType("Ta7", G[P, T]) # E: ParamSpec "P" is not included in type_params \
1166+
# E: Type variable "T" is not included in type_params
1167+
unbound_ps_alias: Ta7[[int], str] # E: Bracketed expression "[...]" is not valid as a type \
1168+
# N: Did you mean "List[...]"? \
1169+
# E: Bad number of arguments for type alias, expected 0, given 2
1170+
reveal_type(unbound_ps_alias) # N: Revealed type is "__main__.G[Any, Any]"
1171+
1172+
# TODO this does not work yet, it should report unbound P
1173+
# Ta8 = TypeAliasType("Ta8", Callable[P, int])
1174+
# unbound_ps_alias: Ta8[int]
1175+
# reveal_type(unbound_ps_alias)
1176+
11511177

11521178
[builtins fixtures/dict.pyi]
11531179

0 commit comments

Comments
 (0)