diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py index 772f8e838c1d..002030076be4 100644 --- a/mypy/newsemanal/semanal.py +++ b/mypy/newsemanal/semanal.py @@ -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]]] diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index d97e92875977..ae316f3d2730 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -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 diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 5e9a895d803a..7646c1dfacc4 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -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 diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 070528b7495a..c98433cf742d 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -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] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 5150c257b2fd..288c37664d22 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -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]