Skip to content

Commit

Permalink
New analyser: Fix crash in plugins when star import blocks adding a s…
Browse files Browse the repository at this point in the history
…ymbol (#7136)

Fixes #7076

The fix is pretty straightforward. I also add a comment about `missing_names` being per-module, not per namespace.
  • Loading branch information
ilevkivskyi authored Jul 3, 2019
1 parent 0440ca0 commit 00ccb78
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 2 deletions.
6 changes: 6 additions & 0 deletions mypy/newsemanal/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ class NewSemanticAnalyzer(NodeVisitor[None],
# unbound names due to cyclic definitions and should not defer)?
_final_iteration = False
# These names couldn't be added to the symbol table due to incomplete deps.
# Note that missing names are per module, _not_ per namespace. This means that e.g.
# a missing name at global scope will block adding same name at a class scope.
# This should not affect correctness and is purely a performance issue,
# since it can cause unnecessary deferrals.
# Note that a star import adds a special name '*' to the set, this blocks
# adding _any_ names in the current file.
missing_names = None # type: Set[str]
# Callbacks that will be called after semantic analysis to tweak things.
patches = None # type: List[Tuple[int, Callable[[], None]]]
Expand Down
7 changes: 6 additions & 1 deletion mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,12 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext',
if ctx.api.options.new_semantic_analyzer:
# Check if attribute types are ready.
for attr in attributes:
if info[attr.name].type is None and not ctx.api.final_iteration:
node = info.get(attr.name)
if node is None:
# This name is likely blocked by a star import. We don't need to defer because
# defer() is already called by mark_incomplete().
return
if node.type is None and not ctx.api.final_iteration:
ctx.api.defer()
return

Expand Down
9 changes: 8 additions & 1 deletion mypy/plugins/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,14 @@ def collect_attributes(self) -> List[DataclassAttribute]:
if not isinstance(lhs, NameExpr):
continue

node = cls.info.names[lhs.name].node
sym = cls.info.names.get(lhs.name)
if sym is None:
# This name is likely blocked by a star import. We don't need to defer because
# defer() is already called by mark_incomplete().
assert ctx.api.options.new_semantic_analyzer
continue

node = sym.node
if isinstance(node, PlaceholderNode):
# This node is not ready yet.
continue
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -1151,3 +1151,19 @@ class C:

C(0).total = 1 # E: Property "total" defined in "C" is read-only
[builtins fixtures/bool.pyi]

[case testTypeInAttrDeferredStar]
import lib
[file lib.py]
import attr
from other import *

@attr.s
class C:
total = attr.ib(type=int)

C() # E: Too few arguments for "C"
C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
[file other.py]
import lib
[builtins fixtures/bool.pyi]
16 changes: 16 additions & 0 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -665,3 +665,19 @@ def func() -> int: ...
c: C
c.x = 1 # E: Property "x" defined in "C" is read-only
[builtins fixtures/bool.pyi]

[case testTypeInDataclassDeferredStar]
import lib
[file lib.py]
from dataclasses import dataclass
from other import *

@dataclass
class C:
total: int

C() # E: Too few arguments for "C"
C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
[file other.py]
import lib
[builtins fixtures/bool.pyi]

0 comments on commit 00ccb78

Please sign in to comment.