Skip to content

Commit c1f48a0

Browse files
authored
Second part of #6098 (#6126)
This PR fixes insufficient traversal of synthetic `TypeInfo`s in fine grained mode.
1 parent fa41d19 commit c1f48a0

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

mypy/semanal_pass3.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def visit_class_def(self, tdef: ClassDef) -> None:
192192
self.analyze(tdef.analyzed.info.typeddict_type, tdef.analyzed, warn=True)
193193
elif isinstance(tdef.analyzed, NamedTupleExpr):
194194
self.analyze(tdef.analyzed.info.tuple_type, tdef.analyzed, warn=True)
195-
self.analyze_info(tdef.analyzed.info)
195+
self.analyze_synthetic_info(tdef.analyzed.info)
196196
super().visit_class_def(tdef)
197197
self.analyze_symbol_table(tdef.info.names)
198198
self.scope.leave()
@@ -273,7 +273,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
273273
if analyzed.info:
274274
# Currently NewTypes only have __init__, but to be future proof,
275275
# we analyze all symbols.
276-
self.analyze_info(analyzed.info)
276+
self.analyze_synthetic_info(analyzed.info)
277277
if analyzed.info and analyzed.info.mro:
278278
analyzed.info.mro = [] # Force recomputation
279279
self.sem.calculate_class_mro(analyzed.info.defn)
@@ -288,7 +288,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
288288
self.analyze(analyzed.info.typeddict_type, analyzed, warn=True)
289289
if isinstance(analyzed, NamedTupleExpr):
290290
self.analyze(analyzed.info.tuple_type, analyzed, warn=True)
291-
self.analyze_info(analyzed.info)
291+
self.analyze_synthetic_info(analyzed.info)
292292
if isinstance(s.lvalues[0], RefExpr) and isinstance(s.lvalues[0].node, Var):
293293
self.analyze(s.lvalues[0].node.type, s.lvalues[0].node)
294294
# Subclass attribute assignments with no type annotation should be
@@ -472,12 +472,18 @@ def patch2() -> None:
472472

473473
self.patches.append((PRIORITY_TYPEVAR_VALUES, self.make_scoped_patch(patch2)))
474474

475-
def analyze_info(self, info: TypeInfo) -> None:
475+
def analyze_synthetic_info(self, info: TypeInfo) -> None:
476476
# Similar to above but for nodes with synthetic TypeInfos (NamedTuple and NewType).
477477
for name in info.names:
478478
sym = info.names[name]
479479
if isinstance(sym.node, (FuncDef, Decorator)):
480+
# Since we are analyzing a synthetic type info, the methods there
481+
# are not real independent targets, and should be processed when
482+
# the enclosing synthetic type is processed.
483+
old_recurse = self.recurse_into_functions
484+
self.recurse_into_functions = True
480485
self.accept(sym.node)
486+
self.recurse_into_functions = old_recurse
481487
if isinstance(sym.node, Var):
482488
self.analyze(sym.node.type, sym.node)
483489

test-data/unit/fine-grained.test

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8206,6 +8206,65 @@ x = 'no way'
82068206
==
82078207
main:10: error: Incompatible types in assignment (expression has type "str", variable has type "int")
82088208

8209+
[case testNamedTupleForwardFunctionDirect]
8210+
# flags: --ignore-missing-imports
8211+
from typing import NamedTuple
8212+
from b import B
8213+
8214+
NT = NamedTuple('NT', [('x', B)])
8215+
[file b.py.2]
8216+
def func(x): pass
8217+
B = func
8218+
[out]
8219+
==
8220+
main:5: error: Invalid type "b.B"
8221+
8222+
[case testNamedTupleForwardFunctionIndirect]
8223+
# flags: --ignore-missing-imports
8224+
from typing import NamedTuple
8225+
from a import A
8226+
8227+
NT = NamedTuple('NT', [('x', A)])
8228+
[file a.py]
8229+
from b import B
8230+
A = B
8231+
[file b.py.2]
8232+
def func(x): pass
8233+
B = func
8234+
[out]
8235+
==
8236+
main:5: error: Invalid type "a.A"
8237+
8238+
[case testNamedTupleForwardFunctionIndirectReveal]
8239+
# flags: --ignore-missing-imports
8240+
import m
8241+
[file m.py]
8242+
from typing import NamedTuple
8243+
from a import A
8244+
8245+
NT = NamedTuple('NT', [('x', A)])
8246+
[file m.py.3]
8247+
from typing import NamedTuple
8248+
from a import A
8249+
8250+
NT = NamedTuple('NT', [('x', A)])
8251+
reveal_type(NT.x)
8252+
x: NT
8253+
reveal_type(x.x)
8254+
[file a.py]
8255+
from b import B
8256+
A = B
8257+
[file b.py.2]
8258+
def func(x): pass
8259+
B = func
8260+
[out]
8261+
==
8262+
m.py:4: error: Invalid type "a.A"
8263+
==
8264+
m.py:4: error: Invalid type "a.A"
8265+
m.py:5: error: Revealed type is 'A?'
8266+
m.py:7: error: Revealed type is 'A?'
8267+
82098268
[case testAliasForwardFunctionDirect]
82108269
# flags: --ignore-missing-imports
82118270
from typing import Optional

0 commit comments

Comments
 (0)