Skip to content

Commit 891e035

Browse files
authored
Fix crash on star unpacking to underscore (#14624)
Fixes #14250 This one is interesting. It looks like most likely this was caused by my PR #7127 that fixed other crash. After looking a bit more, `StarType` is something old, and should never by used. At least I didn't find `visit_star_type()` in any of the type visitors. Actually mypy already uses `assert False`, if we get to a non-special-cased star expression. Btw, I noticed that `pythoneval` test with empty expected output passes in case of a crash (at least on my machine), so I fix this too.
1 parent f6a8037 commit 891e035

File tree

10 files changed

+37
-79
lines changed

10 files changed

+37
-79
lines changed

mypy/checker.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@
192192
Overloaded,
193193
PartialType,
194194
ProperType,
195-
StarType,
196195
TupleType,
197196
Type,
198197
TypeAliasType,
@@ -3288,7 +3287,7 @@ def check_assignment_to_multiple_lvalues(
32883287
last_idx: int | None = None
32893288
for idx_rval, rval in enumerate(rvalue.items):
32903289
if isinstance(rval, StarExpr):
3291-
typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type)
3290+
typs = get_proper_type(self.expr_checker.accept(rval.expr))
32923291
if isinstance(typs, TupleType):
32933292
rvalues.extend([TempNode(typ) for typ in typs.items])
32943293
elif self.type_is_iterable(typs) and isinstance(typs, Instance):
@@ -3311,7 +3310,7 @@ def check_assignment_to_multiple_lvalues(
33113310
iterable_end: int | None = None
33123311
for i, rval in enumerate(rvalues):
33133312
if isinstance(rval, StarExpr):
3314-
typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type)
3313+
typs = get_proper_type(self.expr_checker.accept(rval.expr))
33153314
if self.type_is_iterable(typs) and isinstance(typs, Instance):
33163315
if iterable_start is None:
33173316
iterable_start = i
@@ -3674,8 +3673,7 @@ def check_lvalue(self, lvalue: Lvalue) -> tuple[Type | None, IndexExpr | None, V
36743673
]
36753674
lvalue_type = TupleType(types, self.named_type("builtins.tuple"))
36763675
elif isinstance(lvalue, StarExpr):
3677-
typ, _, _ = self.check_lvalue(lvalue.expr)
3678-
lvalue_type = StarType(typ) if typ else None
3676+
lvalue_type, _, _ = self.check_lvalue(lvalue.expr)
36793677
else:
36803678
lvalue_type = self.expr_checker.accept(lvalue)
36813679

mypy/checkexpr.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@
140140
ParamSpecType,
141141
PartialType,
142142
ProperType,
143-
StarType,
144143
TupleType,
145144
Type,
146145
TypeAliasType,
@@ -5160,8 +5159,9 @@ def visit_typeddict_expr(self, e: TypedDictExpr) -> Type:
51605159
def visit__promote_expr(self, e: PromoteExpr) -> Type:
51615160
return e.type
51625161

5163-
def visit_star_expr(self, e: StarExpr) -> StarType:
5164-
return StarType(self.accept(e.expr))
5162+
def visit_star_expr(self, e: StarExpr) -> Type:
5163+
# TODO: should this ever be called (see e.g. mypyc visitor)?
5164+
return self.accept(e.expr)
51655165

51665166
def object_type(self) -> Instance:
51675167
"""Return instance type 'object'."""

mypy/semanal.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@
257257
ParamSpecType,
258258
PlaceholderType,
259259
ProperType,
260-
StarType,
261260
TrivialSyntheticTypeTranslator,
262261
TupleType,
263262
Type,
@@ -3873,8 +3872,6 @@ def check_lvalue_validity(self, node: Expression | SymbolNode | None, ctx: Conte
38733872
self.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, ctx)
38743873

38753874
def store_declared_types(self, lvalue: Lvalue, typ: Type) -> None:
3876-
if isinstance(typ, StarType) and not isinstance(lvalue, StarExpr):
3877-
self.fail("Star type only allowed for starred expressions", lvalue)
38783875
if isinstance(lvalue, RefExpr):
38793876
lvalue.is_inferred_def = False
38803877
if isinstance(lvalue.node, Var):
@@ -3902,10 +3899,7 @@ def store_declared_types(self, lvalue: Lvalue, typ: Type) -> None:
39023899
self.fail("Tuple type expected for multiple variables", lvalue)
39033900
elif isinstance(lvalue, StarExpr):
39043901
# Historical behavior for the old parser
3905-
if isinstance(typ, StarType):
3906-
self.store_declared_types(lvalue.expr, typ.type)
3907-
else:
3908-
self.store_declared_types(lvalue.expr, typ)
3902+
self.store_declared_types(lvalue.expr, typ)
39093903
else:
39103904
# This has been flagged elsewhere as an error, so just ignore here.
39113905
pass

mypy/server/astmerge.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@
9595
PartialType,
9696
PlaceholderType,
9797
RawExpressionType,
98-
StarType,
9998
SyntheticTypeVisitor,
10099
TupleType,
101100
Type,
@@ -519,9 +518,6 @@ def visit_callable_argument(self, typ: CallableArgument) -> None:
519518
def visit_ellipsis_type(self, typ: EllipsisType) -> None:
520519
pass
521520

522-
def visit_star_type(self, typ: StarType) -> None:
523-
typ.type.accept(self)
524-
525521
def visit_uninhabited_type(self, typ: UninhabitedType) -> None:
526522
pass
527523

mypy/test/testpythoneval.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None
8181
# Normalize paths so that the output is the same on Windows and Linux/macOS.
8282
line = line.replace(test_temp_dir + os.sep, test_temp_dir + "/")
8383
output.append(line.rstrip("\r\n"))
84+
if returncode > 1 and not testcase.output:
85+
# Either api.run() doesn't work well in case of a crash, or pytest interferes with it.
86+
# Tweak output to prevent tests with empty expected output to pass in case of a crash.
87+
output.append("!!! Mypy crashed !!!")
8488
if returncode == 0 and not output:
8589
# Execute the program.
8690
proc = subprocess.run(

mypy/type_visitor.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
PartialType,
3636
PlaceholderType,
3737
RawExpressionType,
38-
StarType,
3938
TupleType,
4039
Type,
4140
TypeAliasType,
@@ -153,11 +152,8 @@ def visit_unpack_type(self, t: UnpackType) -> T:
153152
class SyntheticTypeVisitor(TypeVisitor[T]):
154153
"""A TypeVisitor that also knows how to visit synthetic AST constructs.
155154
156-
Not just real types."""
157-
158-
@abstractmethod
159-
def visit_star_type(self, t: StarType) -> T:
160-
pass
155+
Not just real types.
156+
"""
161157

162158
@abstractmethod
163159
def visit_type_list(self, t: TypeList) -> T:
@@ -386,9 +382,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> T:
386382
def visit_literal_type(self, t: LiteralType) -> T:
387383
return self.strategy([])
388384

389-
def visit_star_type(self, t: StarType) -> T:
390-
return t.type.accept(self)
391-
392385
def visit_union_type(self, t: UnionType) -> T:
393386
return self.query_types(t.items)
394387

@@ -529,9 +522,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> bool:
529522
def visit_literal_type(self, t: LiteralType) -> bool:
530523
return self.default
531524

532-
def visit_star_type(self, t: StarType) -> bool:
533-
return t.type.accept(self)
534-
535525
def visit_union_type(self, t: UnionType) -> bool:
536526
return self.query_types(t.items)
537527

mypy/typeanal.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
PlaceholderType,
6565
RawExpressionType,
6666
RequiredType,
67-
StarType,
6867
SyntheticTypeVisitor,
6968
TrivialSyntheticTypeTranslator,
7069
TupleType,
@@ -1031,17 +1030,7 @@ def visit_tuple_type(self, t: TupleType) -> Type:
10311030
code=codes.SYNTAX,
10321031
)
10331032
return AnyType(TypeOfAny.from_error)
1034-
star_count = sum(1 for item in t.items if isinstance(item, StarType))
1035-
if star_count > 1:
1036-
self.fail("At most one star type allowed in a tuple", t)
1037-
if t.implicit:
1038-
return TupleType(
1039-
[AnyType(TypeOfAny.from_error) for _ in t.items],
1040-
self.named_type("builtins.tuple"),
1041-
t.line,
1042-
)
1043-
else:
1044-
return AnyType(TypeOfAny.from_error)
1033+
10451034
any_type = AnyType(TypeOfAny.special_form)
10461035
# If the fallback isn't filled in yet, its type will be the falsey FakeInfo
10471036
fallback = (
@@ -1093,9 +1082,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
10931082
def visit_literal_type(self, t: LiteralType) -> Type:
10941083
return t
10951084

1096-
def visit_star_type(self, t: StarType) -> Type:
1097-
return StarType(self.anal_type(t.type), t.line)
1098-
10991085
def visit_union_type(self, t: UnionType) -> Type:
11001086
if (
11011087
t.uses_pep604_syntax is True

mypy/types.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,28 +2592,6 @@ def is_singleton_type(self) -> bool:
25922592
return self.is_enum_literal() or isinstance(self.value, bool)
25932593

25942594

2595-
class StarType(ProperType):
2596-
"""The star type *type_parameter.
2597-
2598-
This is not a real type but a syntactic AST construct.
2599-
"""
2600-
2601-
__slots__ = ("type",)
2602-
2603-
type: Type
2604-
2605-
def __init__(self, type: Type, line: int = -1, column: int = -1) -> None:
2606-
super().__init__(line, column)
2607-
self.type = type
2608-
2609-
def accept(self, visitor: TypeVisitor[T]) -> T:
2610-
assert isinstance(visitor, SyntheticTypeVisitor)
2611-
return cast(T, visitor.visit_star_type(self))
2612-
2613-
def serialize(self) -> JsonDict:
2614-
assert False, "Synthetic types don't serialize"
2615-
2616-
26172595
class UnionType(ProperType):
26182596
"""The union type Union[T1, ..., Tn] (at least one type argument)."""
26192597

@@ -3185,10 +3163,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> str:
31853163
def visit_literal_type(self, t: LiteralType) -> str:
31863164
return f"Literal[{t.value_repr()}]"
31873165

3188-
def visit_star_type(self, t: StarType) -> str:
3189-
s = t.type.accept(self)
3190-
return f"*{s}"
3191-
31923166
def visit_union_type(self, t: UnionType) -> str:
31933167
s = self.list_str(t.items)
31943168
return f"Union[{s}]"
@@ -3245,9 +3219,6 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type:
32453219
def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
32463220
return t
32473221

3248-
def visit_star_type(self, t: StarType) -> Type:
3249-
return t
3250-
32513222
def visit_type_list(self, t: TypeList) -> Type:
32523223
return t
32533224

mypy/typetraverser.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
PartialType,
2121
PlaceholderType,
2222
RawExpressionType,
23-
StarType,
2423
SyntheticTypeVisitor,
2524
TupleType,
2625
Type,
@@ -115,9 +114,6 @@ def visit_unbound_type(self, t: UnboundType) -> None:
115114
def visit_type_list(self, t: TypeList) -> None:
116115
self.traverse_types(t.items)
117116

118-
def visit_star_type(self, t: StarType) -> None:
119-
t.type.accept(self)
120-
121117
def visit_ellipsis_type(self, t: EllipsisType) -> None:
122118
pass
123119

test-data/unit/pythoneval.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,3 +1901,26 @@ a[x]
19011901
_testNativeIntTypes.py:14: error: Unsupported operand types for + ("i64" and "i32")
19021902
_testNativeIntTypes.py:15: note: Revealed type is "mypy_extensions.i64"
19031903
_testNativeIntTypes.py:16: note: Revealed type is "mypy_extensions.i64"
1904+
1905+
[case testStarUnpackNestedUnderscore]
1906+
from typing import Tuple, Dict, List
1907+
1908+
def crash() -> None:
1909+
d: Dict[int, Tuple[str, int, str]] = {}
1910+
k, (v1, *_) = next(iter(d.items()))
1911+
1912+
def test1() -> None:
1913+
vs: List[str]
1914+
d: Dict[int, Tuple[str, int, int]] = {}
1915+
k, (v1, *vs) = next(iter(d.items()))
1916+
reveal_type(vs)
1917+
1918+
def test2() -> None:
1919+
d: Dict[int, Tuple[str, int, str]] = {}
1920+
k, (v1, *vs) = next(iter(d.items()))
1921+
reveal_type(vs)
1922+
[out]
1923+
_testStarUnpackNestedUnderscore.py:10: error: List item 0 has incompatible type "int"; expected "str"
1924+
_testStarUnpackNestedUnderscore.py:10: error: List item 1 has incompatible type "int"; expected "str"
1925+
_testStarUnpackNestedUnderscore.py:11: note: Revealed type is "builtins.list[builtins.str]"
1926+
_testStarUnpackNestedUnderscore.py:16: note: Revealed type is "builtins.list[builtins.object]"

0 commit comments

Comments
 (0)