Skip to content

Commit 7692f56

Browse files
authored
New semantic analyzer: fix crash related to dataclasses.InitVar (#6984)
Previously InitVar attributes were missing on the second semantic analysis pass, since the dataclasses plugin removes those attributes from the class symbol table. This caused a crash. The fix is to reset the attribute declarations so that they will be re-added to the symbol table on successive semantic analysis passes. Fixes #6955.
1 parent 00b3a0a commit 7692f56

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

mypy/plugins/dataclasses.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Plugin that provides support for dataclasses."""
2+
13
from collections import OrderedDict
24
from typing import Dict, List, Set, Tuple
35

@@ -166,16 +168,26 @@ def transform(self) -> None:
166168
if decorator_arguments['frozen']:
167169
self._freeze(attributes)
168170

169-
# Remove init-only vars from the class.
170-
for attr in attributes:
171-
if attr.is_init_var:
172-
del info.names[attr.name]
171+
self.reset_init_only_vars(info, attributes)
173172

174173
info.metadata['dataclass'] = {
175174
'attributes': OrderedDict((attr.name, attr.serialize()) for attr in attributes),
176175
'frozen': decorator_arguments['frozen'],
177176
}
178177

178+
def reset_init_only_vars(self, info: TypeInfo, attributes: List[DataclassAttribute]) -> None:
179+
"""Remove init-only vars from the class and reset init var declarations."""
180+
for attr in attributes:
181+
if attr.is_init_var:
182+
del info.names[attr.name]
183+
for stmt in info.defn.defs.body:
184+
if isinstance(stmt, AssignmentStmt) and stmt.unanalyzed_type:
185+
lvalue = stmt.lvalues[0]
186+
if isinstance(lvalue, NameExpr) and lvalue.name == attr.name:
187+
# Reset node so that another semantic analysis pass will
188+
# recreate a symbol node for this attribute.
189+
lvalue.node = None
190+
179191
def collect_attributes(self) -> List[DataclassAttribute]:
180192
"""Collect all attributes declared in the dataclass and its parents.
181193

test-data/unit/check-dataclasses.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,26 @@ app.database_name # E: "SpecializedApplication" has no attribute "database_name
520520

521521
[builtins fixtures/list.pyi]
522522

523+
[case testDataclassesInitVarsAndDefer]
524+
# flags: --new-semantic-analyzer
525+
from dataclasses import InitVar, dataclass
526+
527+
defer: Yes
528+
529+
@dataclass
530+
class Application:
531+
name: str
532+
database_name: InitVar[str]
533+
534+
reveal_type(Application) # E: Revealed type is 'def (name: builtins.str, database_name: builtins.str) -> __main__.Application'
535+
app = Application("example", 42) # E: Argument 2 to "Application" has incompatible type "int"; expected "str"
536+
app = Application("example", "apps")
537+
app.name
538+
app.database_name # E: "Application" has no attribute "database_name"
539+
540+
class Yes: ...
541+
[builtins fixtures/list.pyi]
542+
523543
[case testDataclassFactory]
524544
from typing import Type, TypeVar
525545
from dataclasses import dataclass

0 commit comments

Comments
 (0)