Skip to content

Commit 66b96ed

Browse files
authored
Additional validation for TypeVar defaults (PEP 696) (#15442)
Check that `default` type is a subtype of `bound` or one of the constraint types. Add `default` to nodes `__match_args__`. Missed that initially. Ref: #14851
1 parent e14cddb commit 66b96ed

File tree

3 files changed

+21
-3
lines changed

3 files changed

+21
-3
lines changed

mypy/checkexpr.py

+9
Original file line numberDiff line numberDiff line change
@@ -5197,6 +5197,15 @@ def visit_temp_node(self, e: TempNode) -> Type:
51975197
return e.type
51985198

51995199
def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
5200+
p_default = get_proper_type(e.default)
5201+
if not (
5202+
isinstance(p_default, AnyType)
5203+
and p_default.type_of_any == TypeOfAny.from_omitted_generics
5204+
):
5205+
if not is_subtype(p_default, e.upper_bound):
5206+
self.chk.fail("TypeVar default must be a subtype of the bound type", e)
5207+
if e.values and not any(p_default == value for value in e.values):
5208+
self.chk.fail("TypeVar default must be one of the constraint types", e)
52005209
return AnyType(TypeOfAny.special_form)
52015210

52025211
def visit_paramspec_expr(self, e: ParamSpecExpr) -> Type:

mypy/nodes.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2493,7 +2493,7 @@ class TypeVarExpr(TypeVarLikeExpr):
24932493

24942494
__slots__ = ("values",)
24952495

2496-
__match_args__ = ("name", "values", "upper_bound")
2496+
__match_args__ = ("name", "values", "upper_bound", "default")
24972497

24982498
# Value restriction: only types in the list are valid as values. If the
24992499
# list is empty, there is no restriction.
@@ -2541,7 +2541,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarExpr:
25412541
class ParamSpecExpr(TypeVarLikeExpr):
25422542
__slots__ = ()
25432543

2544-
__match_args__ = ("name", "upper_bound")
2544+
__match_args__ = ("name", "upper_bound", "default")
25452545

25462546
def accept(self, visitor: ExpressionVisitor[T]) -> T:
25472547
return visitor.visit_paramspec_expr(self)
@@ -2575,7 +2575,7 @@ class TypeVarTupleExpr(TypeVarLikeExpr):
25752575

25762576
tuple_fallback: mypy.types.Instance
25772577

2578-
__match_args__ = ("name", "upper_bound")
2578+
__match_args__ = ("name", "upper_bound", "default")
25792579

25802580
def __init__(
25812581
self,

test-data/unit/check-typevar-defaults.test

+9
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,12 @@ Ts1 = TypeVarTuple("Ts1", default=2) # E: The default argument to TypeVarTuple m
7272
Ts2 = TypeVarTuple("Ts2", default=int) # E: The default argument to TypeVarTuple must be an Unpacked tuple
7373
Ts3 = TypeVarTuple("Ts3", default=Tuple[int]) # E: The default argument to TypeVarTuple must be an Unpacked tuple
7474
[builtins fixtures/tuple.pyi]
75+
76+
[case testTypeVarDefaultsInvalid2]
77+
from typing import TypeVar, List, Union
78+
79+
T1 = TypeVar("T1", bound=str, default=int) # E: TypeVar default must be a subtype of the bound type
80+
T2 = TypeVar("T2", bound=List[str], default=List[int]) # E: TypeVar default must be a subtype of the bound type
81+
T3 = TypeVar("T3", int, str, default=bytes) # E: TypeVar default must be one of the constraint types
82+
T4 = TypeVar("T4", int, str, default=Union[int, str]) # E: TypeVar default must be one of the constraint types
83+
T5 = TypeVar("T5", float, str, default=int) # E: TypeVar default must be one of the constraint types

0 commit comments

Comments
 (0)