Skip to content

Fix crash on name collision for self attributes #3700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2279,18 +2279,22 @@ class SymbolTableNode:
cross_ref = None # type: Optional[str]
# Was this node created by normalіze_type_alias?
normalized = False # type: bool
# Was this defined by assignment to self attribute?
implicit = False # type: bool

def __init__(self, kind: int, node: Optional[SymbolNode], mod_id: str = None,
typ: 'mypy.types.Type' = None,
module_public: bool = True, normalized: bool = False,
alias_tvars: Optional[List[str]] = None) -> None:
alias_tvars: Optional[List[str]] = None,
implicit: bool = False) -> None:
self.kind = kind
self.node = node
self.type_override = typ
self.mod_id = mod_id
self.module_public = module_public
self.normalized = normalized
self.alias_tvars = alias_tvars
self.implicit = implicit

@property
def fullname(self) -> Optional[str]:
Expand Down Expand Up @@ -2334,6 +2338,10 @@ def serialize(self, prefix: str, name: str) -> JsonDict:
} # type: JsonDict
if not self.module_public:
data['module_public'] = False
if self.normalized:
data['normalized'] = True
if self.implicit:
data['implicit'] = True
if self.kind == MODULE_REF:
assert self.node is not None, "Missing module cross ref in %s for %s" % (prefix, name)
data['cross_ref'] = self.node.fullname()
Expand Down Expand Up @@ -2371,6 +2379,10 @@ def deserialize(cls, data: JsonDict) -> 'SymbolTableNode':
stnode.alias_tvars = data['alias_tvars']
if 'module_public' in data:
stnode.module_public = data['module_public']
if 'normalized' in data:
stnode.normalized = data['normalized']
if 'implicit' in data:
stnode.implicit = data['implicit']
return stnode


Expand Down
7 changes: 5 additions & 2 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1809,11 +1809,12 @@ def analyze_member_lvalue(self, lval: MemberExpr) -> None:
lval.is_def = True
v = Var(lval.name)
v.set_line(lval)
v._fullname = self.qualified_name(lval.name)
v.info = self.type
v.is_ready = False
lval.def_var = v
lval.node = v
self.type.names[lval.name] = SymbolTableNode(MDEF, v)
self.type.names[lval.name] = SymbolTableNode(MDEF, v, implicit=True)
self.check_lvalue_validity(lval.node, lval)

def is_self_member_ref(self, memberexpr: MemberExpr) -> bool:
Expand Down Expand Up @@ -3400,7 +3401,9 @@ def lookup(self, name: str, ctx: Context) -> SymbolTableNode:
return None
# 2. Class attributes (if within class definition)
if self.is_class_scope() and name in self.type.names:
return self.type.names[name]
node = self.type.names[name]
if not node.implicit:
return node
# 3. Local (function) scopes
for table in reversed(self.locals):
if table is not None and name in table:
Expand Down
82 changes: 82 additions & 0 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -2735,3 +2735,85 @@ class A:
pass
a = A()
[builtins fixtures/property.pyi]

[case testClassNamesResolutionCrashAccess]
import mod

[file mod.py]
class C:
def __init__(self) -> None:
self.int = ''

def f(self, f: int) -> None:
pass

[file mod.py.2]
class C:
def __init__(self) -> None:
self.int = ''

def f(self, f: int) -> None:
f.x

[out]
[out2]
tmp/mod.py:6: error: "int" has no attribute "x"

[case testClassNamesResolutionCrashReadCache]
import mod

[file mod.py]
import submod

[file mod.py.2]
from submod import C

c = C()
reveal_type(c.int)
reveal_type(c.y)

[file submod.py]
from typing import List

class C:
def __init__(self) -> None:
self.int = [] # type: List[int]

def f(self, f: int) -> None:
self.y = f

[builtins fixtures/list.pyi]
[out]
[out2]
tmp/mod.py:4: error: Revealed type is 'builtins.list[builtins.int]'
tmp/mod.py:5: error: Revealed type is 'builtins.int'

[case testClassNamesResolutionCrashReveal]
import mod

[file mod.py]
class Foo(object):

def __init__(self) -> None:
self.bytes = b"foo"

def bar(self, f: bytes):
pass

foo = Foo()
foo.bar(b"test")

[file mod.py.2]
class Foo(object):

def __init__(self) -> None:
self.bytes = b"foo"

def bar(self, f: bytes):
reveal_type(f)

foo = Foo()
foo.bar(b"test")
[out]
[out2]
tmp/mod.py:7: error: Revealed type is 'builtins.bytes'