Skip to content

Commit 9ede13b

Browse files
ilevkivskyiGuido van Rossum
authored and
Guido van Rossum
committed
Fix crash on name collision for self attributes (#3700)
Fixes #3687 (fixes both the crash and the underlying name resolution issue). The fix is quite simple, I also noticed that normalized flag for symbol table nodes was not serialized, so I added this too.
1 parent 0f83375 commit 9ede13b

File tree

3 files changed

+100
-3
lines changed

3 files changed

+100
-3
lines changed

mypy/nodes.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2279,18 +2279,22 @@ class SymbolTableNode:
22792279
cross_ref = None # type: Optional[str]
22802280
# Was this node created by normalіze_type_alias?
22812281
normalized = False # type: bool
2282+
# Was this defined by assignment to self attribute?
2283+
implicit = False # type: bool
22822284

22832285
def __init__(self, kind: int, node: Optional[SymbolNode], mod_id: str = None,
22842286
typ: 'mypy.types.Type' = None,
22852287
module_public: bool = True, normalized: bool = False,
2286-
alias_tvars: Optional[List[str]] = None) -> None:
2288+
alias_tvars: Optional[List[str]] = None,
2289+
implicit: bool = False) -> None:
22872290
self.kind = kind
22882291
self.node = node
22892292
self.type_override = typ
22902293
self.mod_id = mod_id
22912294
self.module_public = module_public
22922295
self.normalized = normalized
22932296
self.alias_tvars = alias_tvars
2297+
self.implicit = implicit
22942298

22952299
@property
22962300
def fullname(self) -> Optional[str]:
@@ -2334,6 +2338,10 @@ def serialize(self, prefix: str, name: str) -> JsonDict:
23342338
} # type: JsonDict
23352339
if not self.module_public:
23362340
data['module_public'] = False
2341+
if self.normalized:
2342+
data['normalized'] = True
2343+
if self.implicit:
2344+
data['implicit'] = True
23372345
if self.kind == MODULE_REF:
23382346
assert self.node is not None, "Missing module cross ref in %s for %s" % (prefix, name)
23392347
data['cross_ref'] = self.node.fullname()
@@ -2371,6 +2379,10 @@ def deserialize(cls, data: JsonDict) -> 'SymbolTableNode':
23712379
stnode.alias_tvars = data['alias_tvars']
23722380
if 'module_public' in data:
23732381
stnode.module_public = data['module_public']
2382+
if 'normalized' in data:
2383+
stnode.normalized = data['normalized']
2384+
if 'implicit' in data:
2385+
stnode.implicit = data['implicit']
23742386
return stnode
23752387

23762388

mypy/semanal.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,11 +1809,12 @@ def analyze_member_lvalue(self, lval: MemberExpr) -> None:
18091809
lval.is_def = True
18101810
v = Var(lval.name)
18111811
v.set_line(lval)
1812+
v._fullname = self.qualified_name(lval.name)
18121813
v.info = self.type
18131814
v.is_ready = False
18141815
lval.def_var = v
18151816
lval.node = v
1816-
self.type.names[lval.name] = SymbolTableNode(MDEF, v)
1817+
self.type.names[lval.name] = SymbolTableNode(MDEF, v, implicit=True)
18171818
self.check_lvalue_validity(lval.node, lval)
18181819

18191820
def is_self_member_ref(self, memberexpr: MemberExpr) -> bool:
@@ -3400,7 +3401,9 @@ def lookup(self, name: str, ctx: Context) -> SymbolTableNode:
34003401
return None
34013402
# 2. Class attributes (if within class definition)
34023403
if self.is_class_scope() and name in self.type.names:
3403-
return self.type.names[name]
3404+
node = self.type.names[name]
3405+
if not node.implicit:
3406+
return node
34043407
# 3. Local (function) scopes
34053408
for table in reversed(self.locals):
34063409
if table is not None and name in table:

test-data/unit/check-incremental.test

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2735,3 +2735,85 @@ class A:
27352735
pass
27362736
a = A()
27372737
[builtins fixtures/property.pyi]
2738+
2739+
[case testClassNamesResolutionCrashAccess]
2740+
import mod
2741+
2742+
[file mod.py]
2743+
class C:
2744+
def __init__(self) -> None:
2745+
self.int = ''
2746+
2747+
def f(self, f: int) -> None:
2748+
pass
2749+
2750+
[file mod.py.2]
2751+
class C:
2752+
def __init__(self) -> None:
2753+
self.int = ''
2754+
2755+
def f(self, f: int) -> None:
2756+
f.x
2757+
2758+
[out]
2759+
[out2]
2760+
tmp/mod.py:6: error: "int" has no attribute "x"
2761+
2762+
[case testClassNamesResolutionCrashReadCache]
2763+
import mod
2764+
2765+
[file mod.py]
2766+
import submod
2767+
2768+
[file mod.py.2]
2769+
from submod import C
2770+
2771+
c = C()
2772+
reveal_type(c.int)
2773+
reveal_type(c.y)
2774+
2775+
[file submod.py]
2776+
from typing import List
2777+
2778+
class C:
2779+
def __init__(self) -> None:
2780+
self.int = [] # type: List[int]
2781+
2782+
def f(self, f: int) -> None:
2783+
self.y = f
2784+
2785+
[builtins fixtures/list.pyi]
2786+
[out]
2787+
[out2]
2788+
tmp/mod.py:4: error: Revealed type is 'builtins.list[builtins.int]'
2789+
tmp/mod.py:5: error: Revealed type is 'builtins.int'
2790+
2791+
[case testClassNamesResolutionCrashReveal]
2792+
import mod
2793+
2794+
[file mod.py]
2795+
class Foo(object):
2796+
2797+
def __init__(self) -> None:
2798+
self.bytes = b"foo"
2799+
2800+
def bar(self, f: bytes):
2801+
pass
2802+
2803+
foo = Foo()
2804+
foo.bar(b"test")
2805+
2806+
[file mod.py.2]
2807+
class Foo(object):
2808+
2809+
def __init__(self) -> None:
2810+
self.bytes = b"foo"
2811+
2812+
def bar(self, f: bytes):
2813+
reveal_type(f)
2814+
2815+
foo = Foo()
2816+
foo.bar(b"test")
2817+
[out]
2818+
[out2]
2819+
tmp/mod.py:7: error: Revealed type is 'builtins.bytes'

0 commit comments

Comments
 (0)