diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index a12b6cadeb69b..cac2a83214b8b 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -335,6 +335,12 @@ def semantic_analyze_target(target: str, priority = mypy.build.PRI_LOW if priority <= state.priorities.get(dep, priority): state.priorities[dep] = priority + + # Clear out some stale data to avoid memory leaks and astmerge + # validity check confusion + analyzer.statement = None + del analyzer.cur_mod_node + if analyzer.deferred: return [target], analyzer.incomplete, analyzer.progress else: diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index 5d00acee8c423..587df57e8a085 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -51,7 +51,7 @@ MypyFile, SymbolTable, Block, AssignmentStmt, NameExpr, MemberExpr, RefExpr, TypeInfo, FuncDef, ClassDef, NamedTupleExpr, SymbolNode, Var, Statement, SuperExpr, NewTypeExpr, OverloadedFuncDef, LambdaExpr, TypedDictExpr, EnumCallExpr, FuncBase, TypeAliasExpr, CallExpr, - CastExpr, + CastExpr, TypeAlias, MDEF ) from mypy.traverser import TraverserVisitor @@ -213,7 +213,7 @@ def visit_ref_expr(self, node: RefExpr) -> None: node.node = self.fixup(node.node) if isinstance(node.node, Var): # The Var node may be an orphan and won't otherwise be processed. - fixup_var(node.node, self.replacements) + node.node.accept(self) def visit_namedtuple_expr(self, node: NamedTupleExpr) -> None: super().visit_namedtuple_expr(node) @@ -266,6 +266,10 @@ def visit_var(self, node: Var) -> None: self.fixup_type(node.type) super().visit_var(node) + def visit_type_alias(self, node: TypeAlias) -> None: + self.fixup_type(node.target) + super().visit_type_alias(node) + # Helpers def fixup(self, node: SN) -> SN: @@ -459,13 +463,6 @@ def replace_nodes_in_symbol_table(symbols: SymbolTable, old = node.node replace_object_state(new, old) node.node = new - if isinstance(node.node, Var): + if isinstance(node.node, (Var, TypeAlias)): # Handle them here just in case these aren't exposed through the AST. - # TODO: Is this necessary? - fixup_var(node.node, replacements) - - -def fixup_var(node: Var, replacements: Dict[SymbolNode, SymbolNode]) -> None: - if node.type: - node.type.accept(TypeReplaceVisitor(replacements)) - node.info = cast(TypeInfo, replacements.get(node.info, node.info)) + node.node.accept(NodeReplaceVisitor(replacements)) diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index dcb820bbffc1e..afa450fb5a758 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -3,7 +3,7 @@ from typing import Dict, List, Tuple from typing_extensions import Final -from mypy.nodes import SymbolNode, Var, Decorator, FuncDef +from mypy.nodes import FakeInfo, SymbolNode, Var, Decorator, FuncDef from mypy.server.objgraph import get_reachable_graph, get_path # If True, print more verbose output on failure. @@ -21,6 +21,9 @@ def check_consistency(o: object) -> None: m = {} # type: Dict[str, SymbolNode] for sym in syms: + if isinstance(sym, FakeInfo): + continue + fn = sym.fullname # Skip None names, since they are ambiguous. # TODO: Everything should have a proper full name? diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index d2dc15217096f..a7b45f5ec81f9 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -54,12 +54,18 @@ def isproperty(o: object, attr: str) -> bool: def get_edge_candidates(o: object) -> Iterator[Tuple[object, object]]: + # use getattr because mypyc expects dict, not mappingproxy + if '__getattribute__' in getattr(type(o), '__dict__'): # noqa + return if type(o) not in COLLECTION_TYPE_BLACKLIST: for attr in dir(o): - if attr not in ATTR_BLACKLIST and hasattr(o, attr) and not isproperty(o, attr): - e = getattr(o, attr) - if not type(e) in ATOMIC_TYPE_BLACKLIST: - yield attr, e + try: + if attr not in ATTR_BLACKLIST and hasattr(o, attr) and not isproperty(o, attr): + e = getattr(o, attr) + if not type(e) in ATOMIC_TYPE_BLACKLIST: + yield attr, e + except AssertionError: + pass if isinstance(o, Mapping): for k, v in o.items(): yield k, v @@ -78,7 +84,7 @@ def get_edges(o: object) -> Iterator[Tuple[object, object]]: yield (s, '__closure__'), e.__closure__ # type: ignore if hasattr(e, '__self__'): se = e.__self__ # type: ignore - if se is not o and se is not type(o): + if se is not o and se is not type(o) and hasattr(s, '__self__'): yield s.__self__, se # type: ignore else: if not type(e) in TYPE_BLACKLIST: diff --git a/mypy/traverser.py b/mypy/traverser.py index de43269f31a95..4ce8332fed860 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -64,6 +64,10 @@ def visit_class_def(self, o: ClassDef) -> None: d.accept(self) for base in o.base_type_exprs: base.accept(self) + if o.metaclass: + o.metaclass.accept(self) + for v in o.keywords.values(): + v.accept(self) o.defs.accept(self) if o.analyzed: o.analyzed.accept(self) diff --git a/test-data/unit/deps-classes.test b/test-data/unit/deps-classes.test index e8b2aaa7dccaf..222b428a0ed46 100644 --- a/test-data/unit/deps-classes.test +++ b/test-data/unit/deps-classes.test @@ -246,4 +246,4 @@ def f() -> None: -> m.f -> m.C, m.f -> m.f - -> , m.M + -> , m, m.M diff --git a/test-data/unit/deps-types.test b/test-data/unit/deps-types.test index b13f33fe7017f..d0674dfadceb2 100644 --- a/test-data/unit/deps-types.test +++ b/test-data/unit/deps-types.test @@ -239,7 +239,7 @@ class M(type): pass [out] -> m.C - -> + -> , m -> m [case testMetaclassDepsDeclared_python2] @@ -268,8 +268,8 @@ class M(type): pass [out] -> m.func - -> - -> m + -> , m.func + -> m, m.func [case testMetaclassAttributes_python2] # flags: --py2