From 1e2d15091e8764ea7616c833b5034b7b83814eb4 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 28 Aug 2019 16:58:16 -0700 Subject: [PATCH 1/2] Fix serialization of redefined nodes Currently they will get incorrectly serialized as xrefs because the symbol table name doesn't match the fullname. Fix this. (Though I don't love the way I've fixed it, if somebody has a suggestion for something nicer.) This could cause nondeterministc crashes under python 3.5 when it led to crossrefs to crossrefs, since random dict iteration order meant that the referenced crossref might not be resolved. Add some asserts to catch these things earlier. --- mypy/fixup.py | 1 + mypy/nodes.py | 6 ++++-- mypy/semanal.py | 1 + test-data/unit/fine-grained.test | 24 ++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index c9a45b890b92a..a014f5dc080fd 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -76,6 +76,7 @@ def visit_symbol_table(self, symtab: SymbolTable) -> None: stnode = lookup_qualified_stnode(self.modules, cross_ref, self.allow_missing) if stnode is not None: + assert stnode.node is not None value.node = stnode.node elif not self.allow_missing: assert stnode is not None, "Could not find cross-ref %s" % (cross_ref,) diff --git a/mypy/nodes.py b/mypy/nodes.py index 236cb92c9a0f4..79343f4ad2123 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2986,8 +2986,10 @@ def serialize(self, prefix: str, name: str) -> JsonDict: assert self.node is not None, '%s:%s' % (prefix, name) if prefix is not None: fullname = self.node.fullname() - if (fullname is not None and '.' in fullname and - fullname != prefix + '.' + name + if (fullname is not None and '.' in fullname + and fullname != prefix + '.' + name + # If it only doesn't match because of -redefinition, that is OK + and fullname != prefix + '.' + name.split('-redefinition')[0] and not (isinstance(self.node, Var) and self.node.from_module_getattr)): data['cross_ref'] = fullname diff --git a/mypy/semanal.py b/mypy/semanal.py index df4126cbf30a1..22da6c3b678df 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -3114,6 +3114,7 @@ def process_module_assignment(self, lvals: List[Lvalue], rval: Expression, # never create module alias except on initial var definition elif lval.is_inferred_def: lnode.kind = self.current_symbol_kind() + assert rnode.node is not None lnode.node = rnode.node def process__all__(self, s: AssignmentStmt) -> None: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 551c752964c7b..b55d765d4bce9 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9160,3 +9160,27 @@ Data = Tuple[User, File] [typing fixtures/typing-full.pyi] [out] == + +[case testClassRedef] +# An issue involved serializing these caused crashes in the past + +[file a.py] +class A: + pass + +x = 0 + +[file a.py.2] +class A: + a = A() + +x = '0' + +[file b.py] +from a import A, x + +class A: # type: ignore + pass + +[out] +== From 8e516eb455a01d3a575704faa4cdb7773323710d Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 29 Aug 2019 11:29:29 -0700 Subject: [PATCH 2/2] Fix another nasty issue --- mypy/fixup.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index a014f5dc080fd..09c190620241a 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -57,8 +57,12 @@ def visit_type_info(self, info: TypeInfo) -> None: if info.metaclass_type: info.metaclass_type.accept(self.type_fixer) if info._mro_refs: - info.mro = [lookup_qualified_typeinfo(self.modules, name, self.allow_missing) - for name in info._mro_refs] + # If the class is a "-redefinition", then its + # reference to itself might be busted, so just use the + # info instead of looking up the first element. Ew. + info.mro = [info] + [ + lookup_qualified_typeinfo(self.modules, name, self.allow_missing) + for name in info._mro_refs[1:]] info._mro_refs = None finally: self.current_info = save_info