diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 78afc422add6..d97e92875977 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -13,7 +13,7 @@ TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncDef, is_class_var, TempNode, Decorator, MemberExpr, Expression, SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED, - TypeVarExpr + TypeVarExpr, PlaceholderNode ) from mypy.plugins.common import ( _get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method @@ -270,6 +270,9 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext', # instance level assignments. if attribute.name in ctx.cls.info.names: node = ctx.cls.info.names[attribute.name].node + if isinstance(node, PlaceholderNode): + # This node is not ready yet. + continue assert isinstance(node, Var) node.is_initialized_in_class = False diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dbe3695bf260..5e9a895d803a 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -8,7 +8,7 @@ from mypy.nodes import ( ARG_OPT, ARG_POS, MDEF, Argument, AssignmentStmt, CallExpr, Context, Expression, FuncDef, JsonDict, NameExpr, RefExpr, - SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr + SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr, PlaceholderNode ) from mypy.plugin import ClassDefContext from mypy.plugins.common import add_method, _get_decorator_bool_argument @@ -214,6 +214,9 @@ def collect_attributes(self) -> List[DataclassAttribute]: continue node = cls.info.names[lhs.name].node + if isinstance(node, PlaceholderNode): + # This node is not ready yet. + continue assert isinstance(node, Var) # x: ClassVar[int] is ignored by dataclasses. diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 6202b4e6aed4..070528b7495a 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1107,3 +1107,47 @@ C(43) class Yes: ... [builtins fixtures/exception.pyi] + +[case testTypeInAttrUndefined] +import attr + +@attr.s +class C: + total = attr.ib(type=Bad) # E: Name 'Bad' is not defined +[builtins fixtures/bool.pyi] + +[case testTypeInAttrForwardInRuntime] +import attr + +@attr.s +class C: + total = attr.ib(type=Forward) + +reveal_type(C.total) # N: Revealed type is '__main__.Forward' +C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "Forward" +class Forward: ... +[builtins fixtures/bool.pyi] + +[case testDefaultInAttrForward] +import attr + +@attr.s +class C: + total = attr.ib(default=func()) + +def func() -> int: ... + +C() +C(1) +C(1, 2) # E: Too many arguments for "C" +[builtins fixtures/bool.pyi] + +[case testTypeInAttrUndefinedFrozen] +import attr + +@attr.s(frozen=True) +class C: + total = attr.ib(type=Bad) # E: Name 'Bad' is not defined + +C(0).total = 1 # E: Property "total" defined in "C" is read-only +[builtins fixtures/bool.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index cac18b239d62..5150c257b2fd 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -642,3 +642,26 @@ a < b class Yes: ... [builtins fixtures/list.pyi] + +[case testDataclassFieldDeferred] +from dataclasses import field, dataclass + +@dataclass +class C: + x: int = field(default=func()) + +def func() -> int: ... +C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" +[builtins fixtures/bool.pyi] + +[case testDataclassFieldDeferredFrozen] +from dataclasses import field, dataclass + +@dataclass(frozen=True) +class C: + x: int = field(default=func()) + +def func() -> int: ... +c: C +c.x = 1 # E: Property "x" defined in "C" is read-only +[builtins fixtures/bool.pyi]