Skip to content

Commit d8b884f

Browse files
elazargmsullivan
authored andcommitted
ListExpr is not an lvalue anymore (#4342)
1 parent af0de0b commit d8b884f

File tree

10 files changed

+104
-35
lines changed

10 files changed

+104
-35
lines changed

mypy/fastparse.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -960,8 +960,12 @@ def visit_Name(self, n: ast3.Name) -> NameExpr:
960960

961961
# List(expr* elts, expr_context ctx)
962962
@with_line
963-
def visit_List(self, n: ast3.List) -> ListExpr:
964-
return ListExpr([self.visit(e) for e in n.elts])
963+
def visit_List(self, n: ast3.List) -> Union[ListExpr, TupleExpr]:
964+
expr_list = [self.visit(e) for e in n.elts] # type: List[Expression]
965+
if isinstance(n.ctx, ast3.Store):
966+
# [x, y] = z and (x, y) = z means exactly the same thing
967+
return TupleExpr(expr_list)
968+
return ListExpr(expr_list)
965969

966970
# Tuple(expr* elts, expr_context ctx)
967971
@with_line
@@ -1155,6 +1159,7 @@ def visit_Ellipsis(self, n: ast3.Ellipsis) -> Type:
11551159

11561160
# List(expr* elts, expr_context ctx)
11571161
def visit_List(self, n: ast3.List) -> Type:
1162+
assert isinstance(n.ctx, ast3.Load)
11581163
return self.translate_argument_list(n.elts)
11591164

11601165

mypy/fastparse2.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,8 +912,12 @@ def visit_Name(self, n: ast27.Name) -> NameExpr:
912912

913913
# List(expr* elts, expr_context ctx)
914914
@with_line
915-
def visit_List(self, n: ast27.List) -> ListExpr:
916-
return ListExpr([self.visit(e) for e in n.elts])
915+
def visit_List(self, n: ast27.List) -> Union[ListExpr, TupleExpr]:
916+
expr_list = [self.visit(e) for e in n.elts] # type: List[Expression]
917+
if isinstance(n.ctx, ast27.Store):
918+
# [x, y] = z and (x, y) = z means exactly the same thing
919+
return TupleExpr(expr_list)
920+
return ListExpr(expr_list)
917921

918922
# Tuple(expr* elts, expr_context ctx)
919923
@with_line

mypy/nodes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
156156

157157
# TODO:
158158
# Lvalue = Union['NameExpr', 'MemberExpr', 'IndexExpr', 'SuperExpr', 'StarExpr'
159-
# 'TupleExpr', 'ListExpr']; see #1783.
159+
# 'TupleExpr']; see #1783.
160160
Lvalue = Expression
161161

162162

@@ -1526,7 +1526,9 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
15261526

15271527

15281528
class TupleExpr(Expression):
1529-
"""Tuple literal expression (..., ...)"""
1529+
"""Tuple literal expression (..., ...)
1530+
1531+
Also lvalue sequences (..., ...) and [..., ...]"""
15301532

15311533
items = None # type: List[Expression]
15321534

mypy/semanal.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,7 +1685,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
16851685
self.check_classvar(s)
16861686
s.rvalue.accept(self)
16871687
if s.type:
1688-
allow_tuple_literal = isinstance(s.lvalues[-1], (TupleExpr, ListExpr))
1688+
allow_tuple_literal = isinstance(s.lvalues[-1], TupleExpr)
16891689
s.type = self.anal_type(s.type, allow_tuple_literal=allow_tuple_literal)
16901690
if (self.type and self.type.is_protocol and isinstance(lval, NameExpr) and
16911691
isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs):
@@ -1922,8 +1922,7 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
19221922
self.fail('Unexpected type declaration', lval)
19231923
if not add_global:
19241924
lval.accept(self)
1925-
elif (isinstance(lval, TupleExpr) or
1926-
isinstance(lval, ListExpr)):
1925+
elif isinstance(lval, TupleExpr):
19271926
items = lval.items
19281927
if len(items) == 0 and isinstance(lval, TupleExpr):
19291928
self.fail("can't assign to ()", lval)
@@ -1936,7 +1935,7 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
19361935
else:
19371936
self.fail('Invalid assignment target', lval)
19381937

1939-
def analyze_tuple_or_list_lvalue(self, lval: Union[ListExpr, TupleExpr],
1938+
def analyze_tuple_or_list_lvalue(self, lval: TupleExpr,
19401939
add_global: bool = False,
19411940
explicit_type: bool = False) -> None:
19421941
"""Analyze an lvalue or assignment target that is a list or tuple."""
@@ -2718,7 +2717,7 @@ def is_classvar(self, typ: Type) -> bool:
27182717
def fail_invalid_classvar(self, context: Context) -> None:
27192718
self.fail('ClassVar can only be used for assignments in class body', context)
27202719

2721-
def process_module_assignment(self, lvals: List[Expression], rval: Expression,
2720+
def process_module_assignment(self, lvals: List[Lvalue], rval: Expression,
27222721
ctx: AssignmentStmt) -> None:
27232722
"""Propagate module references across assignments.
27242723
@@ -2729,14 +2728,13 @@ def process_module_assignment(self, lvals: List[Expression], rval: Expression,
27292728
y].
27302729
27312730
"""
2732-
if all(isinstance(v, (TupleExpr, ListExpr)) for v in lvals + [rval]):
2731+
if (isinstance(rval, (TupleExpr, ListExpr))
2732+
and all(isinstance(v, TupleExpr) for v in lvals)):
27332733
# rval and all lvals are either list or tuple, so we are dealing
27342734
# with unpacking assignment like `x, y = a, b`. Mypy didn't
2735-
# understand our all(isinstance(...)), so cast them as
2736-
# Union[TupleExpr, ListExpr] so mypy knows it is safe to access
2737-
# their .items attribute.
2738-
seq_lvals = cast(List[Union[TupleExpr, ListExpr]], lvals)
2739-
seq_rval = cast(Union[TupleExpr, ListExpr], rval)
2735+
# understand our all(isinstance(...)), so cast them as TupleExpr
2736+
# so mypy knows it is safe to access their .items attribute.
2737+
seq_lvals = cast(List[TupleExpr], lvals)
27402738
# given an assignment like:
27412739
# (x, y) = (m, n) = (a, b)
27422740
# we now have:
@@ -2754,7 +2752,7 @@ def process_module_assignment(self, lvals: List[Expression], rval: Expression,
27542752
# If the rval and all lvals are not all of the same length, zip will just ignore
27552753
# extra elements, so no error will be raised here; mypy will later complain
27562754
# about the length mismatch in type-checking.
2757-
elementwise_assignments = zip(seq_rval.items, *[v.items for v in seq_lvals])
2755+
elementwise_assignments = zip(rval.items, *[v.items for v in seq_lvals])
27582756
for rv, *lvs in elementwise_assignments:
27592757
self.process_module_assignment(lvs, rv, ctx)
27602758
elif isinstance(rval, RefExpr):
@@ -3016,7 +3014,7 @@ def visit_for_stmt(self, s: ForStmt) -> None:
30163014
if s.index_type:
30173015
if self.is_classvar(s.index_type):
30183016
self.fail_invalid_classvar(s.index)
3019-
allow_tuple_literal = isinstance(s.index, (TupleExpr, ListExpr))
3017+
allow_tuple_literal = isinstance(s.index, TupleExpr)
30203018
s.index_type = self.anal_type(s.index_type, allow_tuple_literal=allow_tuple_literal)
30213019
self.store_declared_types(s.index, s.index_type)
30223020

@@ -3093,7 +3091,7 @@ def visit_with_stmt(self, s: WithStmt) -> None:
30933091
t = types.pop(0)
30943092
if self.is_classvar(t):
30953093
self.fail_invalid_classvar(n)
3096-
allow_tuple_literal = isinstance(n, (TupleExpr, ListExpr))
3094+
allow_tuple_literal = isinstance(n, TupleExpr)
30973095
t = self.anal_type(t, allow_tuple_literal=allow_tuple_literal)
30983096
new_types.append(t)
30993097
self.store_declared_types(n, t)

mypy/server/deps.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
267267
for i in range(len(items) - 1):
268268
lvalue = items[i]
269269
rvalue = items[i + 1]
270-
if isinstance(lvalue, (TupleExpr, ListExpr)):
270+
if isinstance(lvalue, TupleExpr):
271271
self.add_attribute_dependency_for_expr(rvalue, '__iter__')
272272
if o.type:
273273
for trigger in get_type_triggers(o.type):
@@ -299,7 +299,7 @@ def process_lvalue(self, lvalue: Expression) -> None:
299299
for attr_trigger in self.attribute_triggers(object_type, lvalue.name):
300300
for type_trigger in type_triggers:
301301
self.add_dependency(type_trigger, attr_trigger)
302-
elif isinstance(lvalue, (ListExpr, TupleExpr)):
302+
elif isinstance(lvalue, TupleExpr):
303303
for item in lvalue.items:
304304
self.process_lvalue(item)
305305
# TODO: star lvalue
@@ -338,7 +338,7 @@ def visit_for_stmt(self, o: ForStmt) -> None:
338338
self.add_attribute_dependency_for_expr(o.expr, '__iter__')
339339
self.add_attribute_dependency_for_expr(o.expr, '__getitem__')
340340
self.process_lvalue(o.index)
341-
if isinstance(o.index, (TupleExpr, ListExpr)):
341+
if isinstance(o.index, TupleExpr):
342342
# Process multiple assignment to index variables.
343343
item_type = o.inferred_item_type
344344
if item_type:

mypy/stats.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
123123
for lvalue in o.lvalues:
124124
if isinstance(lvalue, nodes.TupleExpr):
125125
items = lvalue.items
126-
elif isinstance(lvalue, nodes.ListExpr):
127-
items = lvalue.items
128126
else:
129127
items = [lvalue]
130128
for item in items:

test-data/unit/check-tuples.test

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,75 @@ from typing import Tuple
225225
t1 = None # type: Tuple[A, B]
226226
t2 = None # type: Tuple[A, B, A]
227227
a, b = None, None # type: (A, B)
228+
(a1, b1) = None, None # type: Tuple[A, B]
229+
230+
reveal_type(a1) # E: Revealed type is '__main__.A'
231+
reveal_type(b1) # E: Revealed type is '__main__.B'
228232

229233
a, a = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A")
230234
b, b = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
231235
a, b, b = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
232236

233237
a, b = t1
234-
a, b, a = t2
238+
a, b, a1 = t2
239+
240+
class A: pass
241+
class B: pass
242+
[builtins fixtures/tuple.pyi]
243+
244+
[case testMultipleAssignmentWithSquareBracketTuples]
245+
from typing import Tuple
246+
247+
def avoid_confusing_test_parser() -> None:
248+
t1 = None # type: Tuple[A, B]
249+
t2 = None # type: Tuple[A, B, A]
250+
[a, b] = None, None # type: (A, B)
251+
[a1, b1] = None, None # type: Tuple[A, B]
252+
253+
reveal_type(a) # E: Revealed type is '__main__.A'
254+
reveal_type(b) # E: Revealed type is '__main__.B'
255+
reveal_type(a1) # E: Revealed type is '__main__.A'
256+
reveal_type(b1) # E: Revealed type is '__main__.B'
257+
258+
[a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A")
259+
[b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
260+
[a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
261+
262+
[a, b] = t1
263+
[a, b, a1] = t2
264+
265+
[a2, b2] = t1
266+
reveal_type(a2) # E: Revealed type is '__main__.A'
267+
reveal_type(b2) # E: Revealed type is '__main__.B'
268+
269+
class A: pass
270+
class B: pass
271+
[builtins fixtures/tuple.pyi]
272+
273+
[case testMultipleAssignmentWithSquareBracketTuplesPython2]
274+
# flags: --python-version 2.7
275+
from typing import Tuple
276+
277+
def avoid_confusing_test_parser():
278+
# type: () -> None
279+
t1 = None # type: Tuple[A, B]
280+
t2 = None # type: Tuple[A, B, A]
281+
[a, b] = None, None # type: Tuple[A, B]
282+
[a1, b1] = None, None # type: Tuple[A, B]
283+
284+
reveal_type(a1) # E: Revealed type is '__main__.A'
285+
reveal_type(b1) # E: Revealed type is '__main__.B'
286+
287+
[a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A")
288+
[b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
289+
[a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B")
290+
291+
[a, b] = t1
292+
[a, b, a1] = t2
293+
294+
[a2, b2] = t1
295+
reveal_type(a2) # E: Revealed type is '__main__.A'
296+
reveal_type(b2) # E: Revealed type is '__main__.B'
235297

236298
class A: pass
237299
class B: pass

test-data/unit/parse.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ MypyFile:1(
836836
ExpressionStmt:4(
837837
IntExpr(1))))
838838
ForStmt:5(
839-
ListExpr:5(
839+
TupleExpr:5(
840840
NameExpr(x)
841841
TupleExpr:5(
842842
NameExpr(y)

test-data/unit/semanal-classvar.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ x = None # type: Tuple[ClassVar, int]
133133
main:2: error: Invalid type: ClassVar nested inside other type
134134

135135
[case testMultipleLvaluesWithList]
136-
from typing import ClassVar, List
136+
from typing import ClassVar, Tuple
137137
class A:
138-
[x, y] = None, None # type: List[ClassVar]
139-
[builtins fixtures/list.pyi]
138+
[x, y] = None, None # type: Tuple[ClassVar, ClassVar]
139+
[builtins fixtures/tuple.pyi]
140140
[out]
141141
main:3: error: Invalid type: ClassVar nested inside other type
142142

test-data/unit/semanal-statements.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ MypyFile:1(
342342
NameExpr(y [__main__.y]))
343343
IntExpr(1))
344344
AssignmentStmt:6(
345-
ListExpr:6(
345+
TupleExpr:6(
346346
NameExpr(x [__main__.x])
347347
NameExpr(y [__main__.y]))
348348
IntExpr(1))
@@ -407,7 +407,7 @@ MypyFile:1(
407407
NameExpr(x* [__main__.x])
408408
IntExpr(1))
409409
AssignmentStmt:2(
410-
ListExpr:2(
410+
TupleExpr:2(
411411
NameExpr(y* [__main__.y]))
412412
IntExpr(2)))
413413

@@ -468,14 +468,14 @@ MypyFile:1(
468468
AssignmentStmt:2(
469469
TupleExpr:2(
470470
NameExpr(y* [__main__.y])
471-
ListExpr:2(
471+
TupleExpr:2(
472472
NameExpr(x [__main__.x])
473473
NameExpr(z* [__main__.z])))
474474
IntExpr(1))
475475
AssignmentStmt:3(
476-
ListExpr:3(
476+
TupleExpr:3(
477477
NameExpr(p* [__main__.p])
478-
ListExpr:3(
478+
TupleExpr:3(
479479
NameExpr(x [__main__.x])
480480
NameExpr(r* [__main__.r])))
481481
IntExpr(1)))

0 commit comments

Comments
 (0)