From 4b981c8991ee4f8c588dbb3ba488fa30b743565e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Feb 2017 20:17:24 +0100 Subject: [PATCH 1/2] Fix crash on access to local classes --- mypy/semanal.py | 10 +++++- test-data/unit/check-incremental.test | 45 +++++++++++++++++++++++++++ test-data/unit/semanal-classes.test | 4 +-- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index af6ec9be8080..7d3ed113a886 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -847,7 +847,15 @@ def setup_class_def_analysis(self, defn: ClassDef) -> None: kind = MDEF if self.is_func_scope(): kind = LDEF - self.add_symbol(defn.name, SymbolTableNode(kind, defn.info), defn) + node = SymbolTableNode(kind, defn.info) + self.add_symbol(defn.name, node, defn) + if kind == LDEF: + # We need to preserve local classes, let's store them + # in globals under mangled unique names + local_name = defn.info._fullname + '@' + str(defn.line) + defn.info._fullname = self.cur_mod_id + '.' + local_name + defn.fullname = defn.info._fullname + self.globals[local_name] = node def analyze_base_classes(self, defn: ClassDef) -> None: """Analyze and set up base classes. diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index b4c5de57fb12..bdcd6df13149 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1768,6 +1768,51 @@ main:2: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=typing.Mapp main:3: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=ntcrash.C.A@4)' main:4: error: Revealed type is 'def () -> ntcrash.C.A@4' +[case testIncrementalInnerClassAttrInMethod] +import crash +nonexisting +[file crash.py] +class C: + def f(self) -> None: + class A: + pass + self.a = A() +[out1] +main:2: error: Name 'nonexisting' is not defined +[out2] +main:2: error: Name 'nonexisting' is not defined + +[case testIncrementalInnerClassAttrInMethodReveal] +import crash +reveal_type(crash.C().a) +reveal_type(crash.D().a) +[file crash.py] +class C: + def f(self) -> None: + class A: + pass + self.a = A() +reveal_type(C().a) +class D: + def f(self) -> None: + class A: + def g(self) -> None: + class B: + pass + self.b = B() + self.a = A().b +reveal_type(D().a) +[out1] +tmp/crash.py:6: error: Revealed type is 'crash.A@3' +tmp/crash.py:15: error: Revealed type is 'crash.B@11' +main:2: error: Revealed type is 'crash.A@3' +main:3: error: Revealed type is 'crash.B@11' +[out2] +tmp/crash.py:6: error: Revealed type is 'crash.A@3' +tmp/crash.py:15: error: Revealed type is 'crash.B@11' +main:2: error: Revealed type is 'crash.A@3' +main:3: error: Revealed type is 'crash.B@11' + [case testGenericMethodRestoreMetaLevel] from typing import Dict diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index a99f8511ad0c..f292c303b862 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -315,7 +315,7 @@ MypyFile:1( A PassStmt:2()) ExpressionStmt:3( - NameExpr(A [l]))))) + NameExpr(A [__main__.A@2]))))) [case testReferenceToClassWithinClass] class A: @@ -364,7 +364,7 @@ MypyFile:1( AssignmentStmt:3( NameExpr(x [l]) NameExpr(None [builtins.None]) - A)))) + __main__.A@2)))) [case testAccessToLocalInOuterScopeWithinNestedClass] def f(x): From 237c70cb1e39673ddaf17a60e1eee4ff51cdae7c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Feb 2017 20:27:12 +0100 Subject: [PATCH 2/2] Add some generisity to tests --- test-data/unit/check-incremental.test | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index bdcd6df13149..0ce740ac60ed 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1787,6 +1787,8 @@ import crash reveal_type(crash.C().a) reveal_type(crash.D().a) [file crash.py] +from typing import TypeVar, Generic +T = TypeVar('T') class C: def f(self) -> None: class A: @@ -1797,21 +1799,21 @@ class D: def f(self) -> None: class A: def g(self) -> None: - class B: + class B(Generic[T]): pass - self.b = B() + self.b = B[int]() self.a = A().b reveal_type(D().a) [out1] -tmp/crash.py:6: error: Revealed type is 'crash.A@3' -tmp/crash.py:15: error: Revealed type is 'crash.B@11' -main:2: error: Revealed type is 'crash.A@3' -main:3: error: Revealed type is 'crash.B@11' +tmp/crash.py:8: error: Revealed type is 'crash.A@5' +tmp/crash.py:17: error: Revealed type is 'crash.B@13[builtins.int*]' +main:2: error: Revealed type is 'crash.A@5' +main:3: error: Revealed type is 'crash.B@13[builtins.int*]' [out2] -tmp/crash.py:6: error: Revealed type is 'crash.A@3' -tmp/crash.py:15: error: Revealed type is 'crash.B@11' -main:2: error: Revealed type is 'crash.A@3' -main:3: error: Revealed type is 'crash.B@11' +tmp/crash.py:8: error: Revealed type is 'crash.A@5' +tmp/crash.py:17: error: Revealed type is 'crash.B@13[builtins.int*]' +main:2: error: Revealed type is 'crash.A@5' +main:3: error: Revealed type is 'crash.B@13[builtins.int*]' [case testGenericMethodRestoreMetaLevel] from typing import Dict