From 89d53553287f0ad628215d1db3135dce2c79f8b8 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Tue, 8 Oct 2024 22:17:34 +0200 Subject: [PATCH 01/11] Two improvements of the current PEP 702 implementation (@deprecated): * Analyse the explicit type annotations of "normal" assignment statements (that have an rvalue). * Dive into nested type annotations of assignment statements. --- mypy/checker.py | 18 +++++++++++++++--- test-data/unit/check-deprecated.test | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4bbd49cd7198..25cf622465f4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -216,6 +216,7 @@ UnpackType, find_unpack_in_list, flatten_nested_unions, + flatten_nested_tuples, get_proper_type, get_proper_types, is_literal_type, @@ -2930,14 +2931,13 @@ 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) ): - self.check_deprecated(instance.type, s) + self.search_deprecated(var.type, s) # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such @@ -7581,6 +7581,18 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note warn(deprecated, context, code=codes.DEPRECATED) + def search_deprecated(self, typ, s: AssignmentStmt) -> None: + if isinstance(typ := get_proper_type(typ), Instance): + self.check_deprecated(typ.type, s) + for arg in typ.args: + self.search_deprecated(arg, s) + elif isinstance(typ, UnionType): + for subtype in flatten_nested_unions([typ]): + self.search_deprecated(subtype, s) + elif isinstance(typ, TupleType): + for subtype in flatten_types(typ): + self.search_deprecated(subtype, s) + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index f587034d8059..6779065de788 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -102,6 +102,7 @@ def h() -> None: ... [case testDeprecatedClass] +from typing import List, Optional, Tuple, Union from typing_extensions import deprecated @deprecated("use C2 instead") @@ -114,10 +115,30 @@ 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 + [builtins fixtures/tuple.pyi] From e2ccc4ee883577a39520045d65ee7df5f2551de7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 20:28:23 +0000 Subject: [PATCH 02/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checker.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 25cf622465f4..cc48eb331ad8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -216,7 +216,6 @@ UnpackType, find_unpack_in_list, flatten_nested_unions, - flatten_nested_tuples, get_proper_type, get_proper_types, is_literal_type, @@ -2933,10 +2932,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if s.unanalyzed_type is not None: for lvalue in s.lvalues: - if ( - isinstance(lvalue, NameExpr) - and isinstance(var := lvalue.node, Var) - ): + if isinstance(lvalue, NameExpr) and isinstance(var := lvalue.node, Var): self.search_deprecated(var.type, s) # Avoid type checking type aliases in stubs to avoid false From 755b479e2d3b409a727dc04a0dff1cb19928e7b6 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Tue, 8 Oct 2024 23:16:04 +0200 Subject: [PATCH 03/11] some fixes (mainly to avoid recursion errors) --- mypy/checker.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 25cf622465f4..9cf8c2ed4ebe 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -216,7 +216,6 @@ UnpackType, find_unpack_in_list, flatten_nested_unions, - flatten_nested_tuples, get_proper_type, get_proper_types, is_literal_type, @@ -2937,7 +2936,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: isinstance(lvalue, NameExpr) and isinstance(var := lvalue.node, Var) ): - self.search_deprecated(var.type, s) + self.search_deprecated(var.type, s, set()) # Avoid type checking type aliases in stubs to avoid false # positives about modern type syntax available in stubs such @@ -7581,17 +7580,17 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note warn(deprecated, context, code=codes.DEPRECATED) - def search_deprecated(self, typ, s: AssignmentStmt) -> None: - if isinstance(typ := get_proper_type(typ), Instance): - self.check_deprecated(typ.type, s) - for arg in typ.args: - self.search_deprecated(arg, s) - elif isinstance(typ, UnionType): - for subtype in flatten_nested_unions([typ]): - self.search_deprecated(subtype, s) - elif isinstance(typ, TupleType): - for subtype in flatten_types(typ): - self.search_deprecated(subtype, s) + def search_deprecated(self, typ: Type | None, s: AssignmentStmt, visited: set[Type]) -> None: + + if typ not in visited: + visited.add(typ) + if isinstance(typ := get_proper_type(typ), Instance): + self.check_deprecated(typ.type, s) + for arg in typ.args: + self.search_deprecated(arg, s, visited) + elif isinstance(typ, (UnionType, TupleType)): + for item in typ.items: + self.search_deprecated(item, s, visited) class CollectArgTypeVarTypes(TypeTraverserVisitor): From 466ee434c4f4a17d7f0b716d8dd191154f340e34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 21:22:16 +0000 Subject: [PATCH 04/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5244e57cb775..ecccc5932a08 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7582,12 +7582,12 @@ def search_deprecated(self, typ: Type | None, s: AssignmentStmt, visited: set[Ty if typ not in visited: visited.add(typ) if isinstance(typ := get_proper_type(typ), Instance): - self.check_deprecated(typ.type, s) - for arg in typ.args: - self.search_deprecated(arg, s, visited) + self.check_deprecated(typ.type, s) + for arg in typ.args: + self.search_deprecated(arg, s, visited) elif isinstance(typ, (UnionType, TupleType)): for item in typ.items: - self.search_deprecated(item, s, visited) + self.search_deprecated(item, s, visited) class CollectArgTypeVarTypes(TypeTraverserVisitor): From 55b4126009b34a68e7ebc01a252f9b954fb32956 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Tue, 8 Oct 2024 23:24:58 +0200 Subject: [PATCH 05/11] fix type hint --- mypy/checker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index ecccc5932a08..05166323ce5e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7577,7 +7577,9 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note warn(deprecated, context, code=codes.DEPRECATED) - def search_deprecated(self, typ: Type | None, s: AssignmentStmt, visited: set[Type]) -> None: + def search_deprecated( + self, typ: Type | None, s: AssignmentStmt, visited: set[Type | None] + ) -> None: if typ not in visited: visited.add(typ) From f8f8d7358963a4576cb754b073ca3bfecff0b113 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Thu, 10 Oct 2024 08:25:37 +0200 Subject: [PATCH 06/11] `search_deprecated` -> `InstanceDeprecatedVisitor` (and additionally `CallableType` and `TypeAliasType`) --- mypy/checker.py | 102 ++++++++++++++++++++++----- test-data/unit/check-deprecated.test | 9 ++- 2 files changed, 93 insertions(+), 18 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 05166323ce5e..2728b971ab76 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -196,6 +196,8 @@ LiteralType, NoneType, Overloaded, + Parameters, + ParamSpecType, PartialType, ProperType, TupleType, @@ -224,6 +226,7 @@ from mypy.types_utils import is_overlapping_none, remove_optional, store_argument_type, strip_type from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars, fill_typevars_with_any, has_no_typevars +from mypy.type_visitor import TypeVisitor from mypy.util import is_dunder, is_sunder from mypy.visitor import NodeVisitor @@ -287,6 +290,83 @@ class PartialTypeScope(NamedTuple): is_local: bool +class InstanceDeprecatedVisitor(TypeVisitor[None]): + """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_any(self, t: AnyType) -> None: + pass + + def visit_callable_type(self, t: CallableType) -> None: + for arg_type in t.arg_types: + arg_type.accept(self) + t.ret_type.accept(self) + + def visit_deleted_type(self, t: DeletedType) -> None: + pass + + def visit_erased_type(self, t: ErasedType) -> None: + pass + + def visit_instance(self, t: Instance) -> None: + self.typechecker.check_deprecated(t.type, self.context) + for arg in t.args: + arg.accept(self) + + def visit_literal_type(self, t: LiteralType) -> None: + pass + + def visit_none_type(self, t: NoneType) -> None: + pass + + def visit_overloaded(self, t: Overloaded) -> None: + pass + + def visit_param_spec(self, t: ParamSpecType) -> None: + pass + + def visit_parameters(self, t: Parameters) -> None: + pass + + def visit_partial_type(self, t: PartialType) -> None: + pass + + def visit_tuple_type(self, t: TupleType) -> None: + for item in t.items: + item.accept(self) + + def visit_type_alias_type(self, t: TypeAliasType) -> None: + t.alias.target.accept(self) + + def visit_type_type(self, t: TypeType) -> None: + pass + + def visit_type_var(self, t: TypeVarType) -> None: + pass + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + pass + + def visit_typeddict_type(self, t: TypedDictType) -> None: + pass + + def visit_unbound_type(self, t: UnboundType) -> None: + pass + + def visit_uninhabited_type(self, t: UninhabitedType) -> None: + pass + + def visit_union_type(self, t: UnionType) -> None: + for item in t.items: + item.accept(self) + + def visit_unpack_type(self, t: UnpackType) -> None: + pass + + class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. @@ -2932,8 +3012,12 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if s.unanalyzed_type is not None: for lvalue in s.lvalues: - if isinstance(lvalue, NameExpr) and isinstance(var := lvalue.node, Var): - self.search_deprecated(var.type, s, set()) + if ( + isinstance(lvalue, NameExpr) + and isinstance(var := lvalue.node, Var) + and (var.type is not None) + ): + 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 @@ -7577,20 +7661,6 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None: warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note warn(deprecated, context, code=codes.DEPRECATED) - def search_deprecated( - self, typ: Type | None, s: AssignmentStmt, visited: set[Type | None] - ) -> None: - - if typ not in visited: - visited.add(typ) - if isinstance(typ := get_proper_type(typ), Instance): - self.check_deprecated(typ.type, s) - for arg in typ.args: - self.search_deprecated(arg, s, visited) - elif isinstance(typ, (UnionType, TupleType)): - for item in typ.items: - self.search_deprecated(item, s, visited) - class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 6779065de788..f244565eeed5 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -102,8 +102,8 @@ def h() -> None: ... [case testDeprecatedClass] -from typing import List, Optional, Tuple, Union -from typing_extensions import deprecated +from typing import Callable, List, Optional, Tuple, Union +from typing_extensions import deprecated, TypeAlias @deprecated("use C2 instead") class C: ... @@ -138,6 +138,11 @@ 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 + +A: TypeAlias = Optional[C] # ToDo +x11: A # N: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] From 8547b2efc45b13bf7234df2f2f81fc5e93775a21 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 06:26:26 +0000 Subject: [PATCH 07/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2728b971ab76..d42e21e17f0a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -160,6 +160,7 @@ ) from mypy.traverser import TraverserVisitor, all_return_statements, has_return_statement from mypy.treetransform import TransformVisitor +from mypy.type_visitor import TypeVisitor from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type, make_optional_type from mypy.typeops import ( bind_self, @@ -226,7 +227,6 @@ from mypy.types_utils import is_overlapping_none, remove_optional, store_argument_type, strip_type from mypy.typetraverser import TypeTraverserVisitor from mypy.typevars import fill_typevars, fill_typevars_with_any, has_no_typevars -from mypy.type_visitor import TypeVisitor from mypy.util import is_dunder, is_sunder from mypy.visitor import NodeVisitor From a484f1650c9472f8a4563f04501442ace365de3f Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Thu, 10 Oct 2024 08:45:05 +0200 Subject: [PATCH 08/11] avoid recursion errors --- mypy/checker.py | 35 +++++++++++++++++++--------- test-data/unit/check-deprecated.test | 7 ++++-- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index d42e21e17f0a..9f0374bfe381 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -296,14 +296,22 @@ class InstanceDeprecatedVisitor(TypeVisitor[None]): def __init__(self, typechecker: TypeChecker, context: Context) -> None: self.typechecker = typechecker self.context = context + self.visited: set[Type] = set() + + def _already_visited(self, t: Type, /) -> bool: + if t in self.visited: + return True + self.visited.add(t) + return False def visit_any(self, t: AnyType) -> None: pass def visit_callable_type(self, t: CallableType) -> None: - for arg_type in t.arg_types: - arg_type.accept(self) - t.ret_type.accept(self) + if not self._already_visited(t): + for arg_type in t.arg_types: + arg_type.accept(self) + t.ret_type.accept(self) def visit_deleted_type(self, t: DeletedType) -> None: pass @@ -312,9 +320,10 @@ def visit_erased_type(self, t: ErasedType) -> None: pass def visit_instance(self, t: Instance) -> None: - self.typechecker.check_deprecated(t.type, self.context) - for arg in t.args: - arg.accept(self) + if not self._already_visited(t): + self.typechecker.check_deprecated(t.type, self.context) + for arg in t.args: + arg.accept(self) def visit_literal_type(self, t: LiteralType) -> None: pass @@ -335,11 +344,14 @@ def visit_partial_type(self, t: PartialType) -> None: pass def visit_tuple_type(self, t: TupleType) -> None: - for item in t.items: - item.accept(self) + if not self._already_visited(t): + for item in t.items: + item.accept(self) def visit_type_alias_type(self, t: TypeAliasType) -> None: - t.alias.target.accept(self) + if not self._already_visited(t): + if ((alias := t.alias) is not None) and ((target := alias.target) is not None): + target.accept(self) def visit_type_type(self, t: TypeType) -> None: pass @@ -360,8 +372,9 @@ def visit_uninhabited_type(self, t: UninhabitedType) -> None: pass def visit_union_type(self, t: UnionType) -> None: - for item in t.items: - item.accept(self) + if not self._already_visited(t): + for item in t.items: + item.accept(self) def visit_unpack_type(self, t: UnpackType) -> None: pass diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index f244565eeed5..4ff95fa42d3b 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -141,8 +141,11 @@ x8: List[Optional[Tuple[Union[List[C], int]]]] # N: class __main__.C is depreca 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 -A: TypeAlias = Optional[C] # ToDo -x11: A # N: class __main__.C is deprecated: use C2 instead +A1: TypeAlias = Optional[C] # ToDo +x11: A1 # N: class __main__.C is deprecated: use C2 instead + +A2: TypeAlias = List[Union[A2, C]] # ToDo +x12: A2 # N: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi] From 6e9d4b4c7df9d8c97dca8e047bea1452f64b27e0 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Thu, 10 Oct 2024 22:03:09 +0200 Subject: [PATCH 09/11] Derive `InstanceDeprecatedVisitor` from `TypeTraverserVisitor` instead of `TypeVisitor`. --- mypy/checker.py | 85 ++++--------------------------------------------- 1 file changed, 7 insertions(+), 78 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9f0374bfe381..e07fce47cca4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -160,7 +160,6 @@ ) from mypy.traverser import TraverserVisitor, all_return_statements, has_return_statement from mypy.treetransform import TransformVisitor -from mypy.type_visitor import TypeVisitor from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type, make_optional_type from mypy.typeops import ( bind_self, @@ -290,95 +289,25 @@ class PartialTypeScope(NamedTuple): is_local: bool -class InstanceDeprecatedVisitor(TypeVisitor[None]): +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 - self.visited: set[Type] = set() - - def _already_visited(self, t: Type, /) -> bool: - if t in self.visited: - return True - self.visited.add(t) - return False - - def visit_any(self, t: AnyType) -> None: - pass - - def visit_callable_type(self, t: CallableType) -> None: - if not self._already_visited(t): - for arg_type in t.arg_types: - arg_type.accept(self) - t.ret_type.accept(self) - - def visit_deleted_type(self, t: DeletedType) -> None: - pass - - def visit_erased_type(self, t: ErasedType) -> None: - pass + self.seen_aliases: set[TypeAliasType] = set() def visit_instance(self, t: Instance) -> None: - if not self._already_visited(t): - self.typechecker.check_deprecated(t.type, self.context) - for arg in t.args: - arg.accept(self) - - def visit_literal_type(self, t: LiteralType) -> None: - pass - - def visit_none_type(self, t: NoneType) -> None: - pass - - def visit_overloaded(self, t: Overloaded) -> None: - pass - - def visit_param_spec(self, t: ParamSpecType) -> None: - pass - - def visit_parameters(self, t: Parameters) -> None: - pass - - def visit_partial_type(self, t: PartialType) -> None: - pass - - def visit_tuple_type(self, t: TupleType) -> None: - if not self._already_visited(t): - for item in t.items: - item.accept(self) + super().visit_instance(t) + self.typechecker.check_deprecated(t.type, self.context) def visit_type_alias_type(self, t: TypeAliasType) -> None: - if not self._already_visited(t): + super().visit_type_alias_type(t) + if t not in self.seen_aliases: + self.seen_aliases.add(t) if ((alias := t.alias) is not None) and ((target := alias.target) is not None): target.accept(self) - def visit_type_type(self, t: TypeType) -> None: - pass - - def visit_type_var(self, t: TypeVarType) -> None: - pass - - def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: - pass - - def visit_typeddict_type(self, t: TypedDictType) -> None: - pass - - def visit_unbound_type(self, t: UnboundType) -> None: - pass - - def visit_uninhabited_type(self, t: UninhabitedType) -> None: - pass - - def visit_union_type(self, t: UnionType) -> None: - if not self._already_visited(t): - for item in t.items: - item.accept(self) - - def visit_unpack_type(self, t: UnpackType) -> None: - pass - class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. From 90039d8c7046581438baf28e5bb5b575c1557c03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:04:53 +0000 Subject: [PATCH 10/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checker.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e07fce47cca4..c7c44e41f7d1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -196,8 +196,6 @@ LiteralType, NoneType, Overloaded, - Parameters, - ParamSpecType, PartialType, ProperType, TupleType, From 98fea8c600b0a9dda6c24f6844f6c624bc003fc2 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Fri, 11 Oct 2024 20:31:47 +0200 Subject: [PATCH 11/11] Do not report using indirectly deprecated aliases. --- mypy/checker.py | 8 -------- test-data/unit/check-deprecated.test | 10 ++++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c7c44e41f7d1..ecc714b7d7cd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -293,19 +293,11 @@ class InstanceDeprecatedVisitor(TypeTraverserVisitor): def __init__(self, typechecker: TypeChecker, context: Context) -> None: self.typechecker = typechecker self.context = context - self.seen_aliases: set[TypeAliasType] = set() def visit_instance(self, t: Instance) -> None: super().visit_instance(t) self.typechecker.check_deprecated(t.type, self.context) - def visit_type_alias_type(self, t: TypeAliasType) -> None: - super().visit_type_alias_type(t) - if t not in self.seen_aliases: - self.seen_aliases.add(t) - if ((alias := t.alias) is not None) and ((target := alias.target) is not None): - target.accept(self) - class TypeChecker(NodeVisitor[None], CheckerPluginInterface): """Mypy type checker. diff --git a/test-data/unit/check-deprecated.test b/test-data/unit/check-deprecated.test index 4ff95fa42d3b..13cebc85513e 100644 --- a/test-data/unit/check-deprecated.test +++ b/test-data/unit/check-deprecated.test @@ -103,7 +103,7 @@ def h() -> None: ... [case testDeprecatedClass] from typing import Callable, List, Optional, Tuple, Union -from typing_extensions import deprecated, TypeAlias +from typing_extensions import deprecated, TypeAlias, TypeVar @deprecated("use C2 instead") class C: ... @@ -141,11 +141,13 @@ x8: List[Optional[Tuple[Union[List[C], int]]]] # N: class __main__.C is depreca 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 # N: class __main__.C is deprecated: use C2 instead - +x11: A1 A2: TypeAlias = List[Union[A2, C]] # ToDo -x12: A2 # N: class __main__.C is deprecated: use C2 instead +x12: A2 +A3: TypeAlias = List[Optional[T]] +x13: A3[C] # N: class __main__.C is deprecated: use C2 instead [builtins fixtures/tuple.pyi]