diff --git a/mypy/checker.py b/mypy/checker.py index 4bbd49cd7198..ecc714b7d7cd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -287,6 +287,18 @@ class PartialTypeScope(NamedTuple): is_local: bool +class InstanceDeprecatedVisitor(TypeTraverserVisitor): + """Visitor that recursively checks for deprecations in nested instances.""" + + def __init__(self, typechecker: TypeChecker, context: Context) -> None: + self.typechecker = typechecker + self.context = context + + def visit_instance(self, t: Instance) -> None: + super().visit_instance(t) + self.typechecker.check_deprecated(t.type, self.context) + + class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. @@ -2930,14 +2942,14 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: Handle all kinds of assignment statements (simple, indexed, multiple). """ - if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if s.unanalyzed_type is not None: for lvalue in s.lvalues: if ( isinstance(lvalue, NameExpr) and isinstance(var := lvalue.node, Var) - and isinstance(instance := get_proper_type(var.type), Instance) + and (var.type is not None) ): - self.check_deprecated(instance.type, s) + var.type.accept(InstanceDeprecatedVisitor(typechecker=self, context=s)) # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index f587034d8059..13cebc85513e 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -102,7 +102,8 @@ def h() -> None: ... [case testDeprecatedClass] -from typing_extensions import deprecated +from typing import Callable, List, Optional, Tuple, Union +from typing_extensions import deprecated, TypeAlias, TypeVar @deprecated("use C2 instead") class C: ... @@ -114,10 +115,40 @@ C.missing() # N: class __main__.C is deprecated: use C2 instead \ C.__init__(c) # N: class __main__.C is deprecated: use C2 instead C(1) # N: class __main__.C is deprecated: use C2 instead \ # E: Too many arguments for "C" + D = C # N: class __main__.C is deprecated: use C2 instead D() t = (C, C, D) # N: class __main__.C is deprecated: use C2 instead +u1: Union[C, int] = 1 # N: class __main__.C is deprecated: use C2 instead +u1 = 1 +u2 = 1 # type: Union[C, int] # N: class __main__.C is deprecated: use C2 instead +u2 = 1 + +c1 = c2 = C() # N: class __main__.C is deprecated: use C2 instead +i, c3 = 1, C() # N: class __main__.C is deprecated: use C2 instead + +class E: ... + +x1: Optional[C] # N: class __main__.C is deprecated: use C2 instead +x2: Union[D, C, E] # N: class __main__.C is deprecated: use C2 instead +x3: Union[D, Optional[C], E] # N: class __main__.C is deprecated: use C2 instead +x4: Tuple[D, C, E] # N: class __main__.C is deprecated: use C2 instead +x5: Tuple[Tuple[D, C], E] # N: class __main__.C is deprecated: use C2 instead +x6: List[C] # N: class __main__.C is deprecated: use C2 instead +x7: List[List[C]] # N: class __main__.C is deprecated: use C2 instead +x8: List[Optional[Tuple[Union[List[C], int]]]] # N: class __main__.C is deprecated: use C2 instead +x9: Callable[[int], C] # N: class __main__.C is deprecated: use C2 instead +x10: Callable[[int, C, int], int] # N: class __main__.C is deprecated: use C2 instead + +T = TypeVar("T") +A1: TypeAlias = Optional[C] # ToDo +x11: A1 +A2: TypeAlias = List[Union[A2, C]] # ToDo +x12: A2 +A3: TypeAlias = List[Optional[T]] +x13: A3[C] # N: class __main__.C is deprecated: use C2 instead + [builtins fixtures/tuple.pyi]