Skip to content

Commit

Permalink
Replace class InstanceDeprecatedVisitor (module checker) with met…
Browse files Browse the repository at this point in the history
…hod `TypeAnalyzer.check_and_warn_deprecated` (module `typeanal`).
  • Loading branch information
tyralla committed Oct 25, 2024
1 parent a42b123 commit ede5ae1
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 36 deletions.
36 changes: 0 additions & 36 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,40 +288,6 @@ class PartialTypeScope(NamedTuple):
is_local: bool


class InstanceDeprecatedVisitor(MixedTraverserVisitor):
"""Visitor that recursively checks for deprecations in nested instances."""

def __init__(self, typechecker: TypeChecker) -> None:
self.typechecker = typechecker
self.context: Context | None = None

@contextmanager
def _set_context(self, new: Context, /) -> Iterator[None]:
old = self.context
try:
self.context = new
yield None
finally:
self.context = old

def visit_decorator(self, o: Decorator) -> None:
with self._set_context(o.func):
super().visit_decorator(o)

def visit_func(self, o: FuncItem) -> None:
with self._set_context(o):
super().visit_func(o)

def visit_instance(self, t: Instance) -> None:
super().visit_instance(t)
if t.type and not (
isinstance(defn := self.context, FuncDef)
and defn.info
and (defn.info.fullname == t.type.fullname)
):
self.typechecker.check_deprecated(node=t.type, context=t)


class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
"""Mypy type checker.
Expand Down Expand Up @@ -516,7 +482,6 @@ def check_first_pass(self) -> None:
break
else:
self.accept(d)
self.tree.accept(InstanceDeprecatedVisitor(typechecker=self))

assert not self.current_node_deferred

Expand Down Expand Up @@ -574,7 +539,6 @@ def check_second_pass(
def check_partial(self, node: DeferredNodeType | FineGrainedDeferredNodeType) -> None:
if isinstance(node, MypyFile):
self.check_top_level(node)
self.tree.accept(InstanceDeprecatedVisitor(typechecker=self))
else:
self.recurse_into_functions = True
with self.binder.top_frame_context():
Expand Down
2 changes: 2 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3859,6 +3859,7 @@ def analyze_alias(
self.tvar_scope,
self.plugin,
self.options,
self.cur_mod_node,
self.is_typeshed_stub_file,
allow_placeholder=allow_placeholder,
in_dynamic_func=dynamic,
Expand Down Expand Up @@ -7267,6 +7268,7 @@ def type_analyzer(
tvar_scope,
self.plugin,
self.options,
self.cur_mod_node,
self.is_typeshed_stub_file,
allow_unbound_tvars=allow_unbound_tvars,
allow_tuple_literal=allow_tuple_literal,
Expand Down
22 changes: 22 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
ArgKind,
Context,
Decorator,
ImportFrom,
MypyFile,
ParamSpecExpr,
PlaceholderNode,
Expand Down Expand Up @@ -148,6 +149,7 @@ def analyze_type_alias(
tvar_scope: TypeVarLikeScope,
plugin: Plugin,
options: Options,
cur_mod_node: MypyFile,
is_typeshed_stub: bool,
allow_placeholder: bool = False,
in_dynamic_func: bool = False,
Expand All @@ -167,6 +169,7 @@ def analyze_type_alias(
tvar_scope,
plugin,
options,
cur_mod_node,
is_typeshed_stub,
defining_alias=True,
allow_placeholder=allow_placeholder,
Expand Down Expand Up @@ -213,6 +216,7 @@ def __init__(
tvar_scope: TypeVarLikeScope,
plugin: Plugin,
options: Options,
cur_mod_node: MypyFile,
is_typeshed_stub: bool,
*,
defining_alias: bool = False,
Expand Down Expand Up @@ -266,6 +270,7 @@ def __init__(
self.report_invalid_types = report_invalid_types
self.plugin = plugin
self.options = options
self.cur_mod_node = cur_mod_node
self.is_typeshed_stub = is_typeshed_stub
# Names of type aliases encountered while analysing a type will be collected here.
self.aliases_used: set[str] = set()
Expand Down Expand Up @@ -771,6 +776,21 @@ def get_omitted_any(self, typ: Type, fullname: str | None = None) -> AnyType:
disallow_any = not self.is_typeshed_stub and self.options.disallow_any_generics
return get_omitted_any(disallow_any, self.fail, self.note, typ, self.options, fullname)

def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None:
"""Similar logic to `TypeChecker.check_deprecated` and `TypeChecker.warn_deprecated."""

if (
(deprecated := info.deprecated)
and not self.is_typeshed_stub
and not (self.api.type and (self.api.type.fullname == info.fullname))
):
for imp in self.cur_mod_node.imports:
if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names):
break
else:
warn = self.fail if self.options.report_deprecated_as_error else self.note
warn(deprecated, ctx, code=codes.DEPRECATED)

def analyze_type_with_type_info(
self, info: TypeInfo, args: Sequence[Type], ctx: Context, empty_tuple_index: bool
) -> Type:
Expand All @@ -779,6 +799,8 @@ def analyze_type_with_type_info(
This handles simple cases like 'int', 'modname.UserClass[str]', etc.
"""

self.check_and_warn_deprecated(info, ctx)

if len(args) > 0 and info.fullname == "builtins.tuple":
fallback = Instance(info, [AnyType(TypeOfAny.special_form)], ctx.line)
return TupleType(self.anal_array(args, allow_unpack=True), fallback, ctx.line)
Expand Down

0 comments on commit ede5ae1

Please sign in to comment.