Skip to content

Commit 9c00978

Browse files
authored
Fix a handful of fine-grained incremental bugs (python#4691)
* Fix refresh of implicit tuple types * Fix refresh of imports in if MYPY else blocks * Fix astmerge of CastExpr
1 parent 621b7d3 commit 9c00978

File tree

5 files changed

+140
-1
lines changed

5 files changed

+140
-1
lines changed

mypy/server/astmerge.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
Node, MypyFile, SymbolTable, Block, AssignmentStmt, NameExpr, MemberExpr, RefExpr, TypeInfo,
5252
FuncDef, ClassDef, NamedTupleExpr, SymbolNode, Var, Statement, SuperExpr, NewTypeExpr,
5353
OverloadedFuncDef, LambdaExpr, TypedDictExpr, EnumCallExpr, FuncBase, TypeAliasExpr, CallExpr,
54+
CastExpr,
5455
MDEF
5556
)
5657
from mypy.traverser import TraverserVisitor
@@ -217,6 +218,10 @@ def visit_namedtuple_expr(self, node: NamedTupleExpr) -> None:
217218
node.info = self.fixup_and_reset_typeinfo(node.info)
218219
self.process_synthetic_type_info(node.info)
219220

221+
def visit_cast_expr(self, node: CastExpr) -> None:
222+
super().visit_cast_expr(node)
223+
self.fixup_type(node.type)
224+
220225
def visit_super_expr(self, node: SuperExpr) -> None:
221226
super().visit_super_expr(node)
222227
if node.info is not None:
@@ -366,7 +371,9 @@ def visit_partial_type(self, typ: PartialType) -> None:
366371
def visit_tuple_type(self, typ: TupleType) -> None:
367372
for item in typ.items:
368373
item.accept(self)
369-
typ.fallback.accept(self)
374+
# Fallback can be None for implicit tuple types that haven't been semantically analyzed.
375+
if typ.fallback is not None:
376+
typ.fallback.accept(self)
370377

371378
def visit_type_type(self, typ: TypeType) -> None:
372379
typ.item.accept(self)

mypy/server/aststrip.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ def visit_import_from(self, node: ImportFrom) -> None:
159159
if node.assignments:
160160
node.assignments = []
161161
else:
162+
# If the node is unreachable, don't reset entries: they point to something else!
163+
if node.is_unreachable: return
162164
if self.names:
163165
# Reset entries in the symbol table. This is necessary since
164166
# otherwise the semantic analyzer will think that the import
@@ -173,6 +175,8 @@ def visit_import(self, node: Import) -> None:
173175
if node.assignments:
174176
node.assignments = []
175177
else:
178+
# If the node is unreachable, don't reset entries: they point to something else!
179+
if node.is_unreachable: return
176180
if self.names:
177181
# Reset entries in the symbol table. This is necessary since
178182
# otherwise the semantic analyzer will think that the import

test-data/unit/fine-grained-modules.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,3 +1089,23 @@ def foo(x: int) -> None: pass
10891089
==
10901090
a.py:2: error: Too many arguments for "foo"
10911091
==
1092+
1093+
[case testRefreshImportIfMypyElse1]
1094+
import a
1095+
[file a.py]
1096+
from b import foo
1097+
1098+
MYPY = False
1099+
if MYPY:
1100+
x = 0
1101+
else:
1102+
from fictional import x
1103+
1104+
x = 1
1105+
[file b/__init__.py]
1106+
[file b/foo.py]
1107+
[file b/__init__.py.2]
1108+
# Dummy change
1109+
[builtins fixtures/bool.pyi]
1110+
[out]
1111+
==

test-data/unit/fine-grained.test

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2635,3 +2635,84 @@ def a(f: Callable[[Arg(int, 'x')], int]) -> None: pass
26352635
[builtins fixtures/dict.pyi]
26362636
[out]
26372637
==
2638+
2639+
[case testImplicitTuple1]
2640+
import a
2641+
[file a.py]
2642+
# Bogus annotation in nested function masked because outer function
2643+
# isn't annotated
2644+
def unchecked():
2645+
def inner():
2646+
# type: () -> (str, int)
2647+
return 'lol', 10
2648+
[file a.py.2]
2649+
# dummy change
2650+
def unchecked():
2651+
def inner():
2652+
# type: () -> (str, int)
2653+
return 'lol', 10
2654+
[out]
2655+
==
2656+
2657+
[case testImplicitTuple2]
2658+
import a
2659+
[file a.py]
2660+
def inner():
2661+
# type: () -> (str, int)
2662+
return 'lol', 10
2663+
[file a.py.2]
2664+
# dummy change
2665+
def inner():
2666+
# type: () -> (str, int)
2667+
return 'lol', 10
2668+
[out]
2669+
a.py:1: error: Invalid tuple literal type
2670+
==
2671+
a.py:2: error: Invalid tuple literal type
2672+
2673+
[case testImplicitTuple3]
2674+
import a
2675+
[file a.py]
2676+
(x, y) = 1, 'hi' # type: (int, str)
2677+
[file a.py.2]
2678+
# dummy change
2679+
(x, y) = 1, 'hi' # type: (int, str)
2680+
[out]
2681+
==
2682+
2683+
[case testCastConfusion]
2684+
import b
2685+
[file a.py]
2686+
from typing import cast
2687+
class Thing:
2688+
def foo(self) -> None: pass
2689+
2690+
thing = cast(Thing, Thing())
2691+
2692+
[file b.py]
2693+
from typing import Optional
2694+
from a import Thing, thing
2695+
class User:
2696+
def __init__(self, x: Optional[Thing]) -> None:
2697+
self.x = x if x else thing
2698+
def use(self) -> None: self.x.foo()
2699+
2700+
[file a.py.2]
2701+
from typing import cast
2702+
class Thing:
2703+
def foo(self) -> None: pass
2704+
2705+
thing = cast(Thing, Thing())
2706+
# update
2707+
2708+
[file b.py.2]
2709+
from typing import Optional
2710+
from a import Thing, thing
2711+
class User:
2712+
def __init__(self, x: Optional[Thing]) -> None:
2713+
self.x = x if x else thing
2714+
def use(self) -> None: self.x.foo()
2715+
# update
2716+
[builtins fixtures/ops.pyi]
2717+
[out]
2718+
==

test-data/unit/merge.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,3 +1395,30 @@ TypeInfo<2>(
13951395
Bases(builtins.type<3>)
13961396
Mro(target.M<2>, builtins.type<3>, builtins.object<1>)
13971397
Names())
1398+
1399+
[case testCast_symtable]
1400+
import target
1401+
[file target.py]
1402+
from typing import cast
1403+
class Thing:
1404+
pass
1405+
thing = cast(Thing, Thing())
1406+
[file target.py.next]
1407+
from typing import cast
1408+
class Thing:
1409+
pass
1410+
thing = cast(Thing, Thing())
1411+
[out]
1412+
__main__:
1413+
target: MypyFile<0>
1414+
target:
1415+
Thing: TypeInfo<1>
1416+
cast: Var<2>
1417+
thing: Var<3>(target.Thing<1>)
1418+
==>
1419+
__main__:
1420+
target: MypyFile<0>
1421+
target:
1422+
Thing: TypeInfo<1>
1423+
cast: Var<2>
1424+
thing: Var<3>(target.Thing<1>)

0 commit comments

Comments
 (0)