8000 New analyser: fix crashes when plugins encounter a placeholder node (… · python/mypy@d8cde02 · GitHub
[go: up one dir, main page]

Skip to content

Commit d8cde02

Browse files
authored
New analyser: fix crashes when plugins encounter a placeholder node (#7132)
Fixes #7098
1 parent 9eb8b77 commit d8cde02

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

mypy/plugins/attrs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncDef,
1414
is_class_var, TempNode, Decorator, MemberExpr, Expression,
1515
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED,
16-
TypeVarExpr
16+
TypeVarExpr, PlaceholderNode
1717
)
1818
from mypy.plugins.common import (
1919
_get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method
@@ -270,6 +270,9 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext',
270270
# instance level assignments.
271271
if attribute.name in ctx.cls.info.names:
272272
node = ctx.cls.info.names[attribute.name].node
273+
if isinstance(node, PlaceholderNode):
274+
# This node is not ready yet.
275+
continue
273276
assert isinstance(node, Var)
274277
node.is_initialized_in_class = False
275278

mypy/plugins/dataclasses.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from mypy.nodes import (
99
ARG_OPT, ARG_POS, MDEF, Argument, AssignmentStmt, CallExpr,
1010
Context, Expression, FuncDef, JsonDict, NameExpr, RefExpr,
11-
SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr
11+
SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr, PlaceholderNode
1212
)
1313
from mypy.plugin import ClassDefContext
1414
from mypy.plugins.common import add_method, _get_decorator_bool_argument
@@ -214,6 +214,9 @@ def collect_attributes(self) -> List[DataclassAttribute]:
214214
continue
215215

216216
node = cls.info.names[lhs.name].node
217+
if isinstance(node, PlaceholderNode):
218+
# This node is not ready yet.
219+
continue
217220
assert isinstance(node, Var)
218221

219222
# x: ClassVar[int] is ignored by dataclasses.

test-data/unit/check-attr.test

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,3 +1107,47 @@ C(43)
11071107

11081108
class Yes: ...
11091109
[builtins fixtures/exception.pyi]
1110+
1111+
[case testTypeInAttrUndefined]
1112+
import attr
1113+
1114+
@attr.s
1115+
class C:
1116+
total = attr.ib(type=Bad) # E: Name 'Bad' is not defined
1117+
[builtins fixtures/bool.pyi]
1118+
1119+
[case testTypeInAttrForwardInRuntime]
1120+
import attr
1121+
1122+
@attr.s
1123+
class C:
1124+
total = attr.ib(type=Forward)
1125+
1126+
reveal_type(C.total) # N: Revealed type is '__main__.Forward'
1127+
C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "Forward"
1128+
class Forward: ...
1129+
[builtins fixtures/bool.pyi]
1130+
1131+
[case testDefaultInAttrForward]
1132+
import attr
1133+
1134+
@attr.s
1135+
class C:
1136+
total = attr.ib(default=func())
1137+
1138+
def func() -> int: ...
1139+
1140+
C()
1141+
C(1)
1142+
C(1, 2) # E: Too many arguments for "C"
1143+
[builtins fixtures/bool.pyi]
1144+
1145+
[case testTypeInAttrUndefinedFrozen]
1146+
import attr
1147+
1148+
@attr.s(frozen=True)
1149+
class C:
1150+
total = attr.ib(type=Bad) # E: Name 'Bad' is not defined
1151+
1152+
C(0).total = 1 # E: Property "total" defined in "C" is read-only
1153+
[builtins fixtures/bool.pyi]

test-data/unit/check-dataclasses.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,26 @@ a < b
642642

643643
class Yes: ...
644644
[builtins fixtures/list.pyi]
645+
646+
[case testDataclassFieldDeferred]
647+
from dataclasses import field, dataclass
648+
649+
@dataclass
650+
class C:
651+
x: int = field(default=func())
652+
653+
def func() -> int: ...
654+
C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int"
655+
[builtins fixtures/bool.pyi]
656+
657+
[case testDataclassFieldDeferredFrozen]
658+
from dataclasses import field, dataclass
659+
660+
@dataclass(frozen=True)
661+
class C:
662+
x: int = field(default=func())
663+
664+
def func() -> int: ...
665+
c: C
666+
c.x = 1 # E: Property "x" defined in "C" is read-only
667+
[builtins fixtures/bool.pyi]

0 commit comments

Comments
 (0)
0