Skip to content

Commit b30a94f

Browse files
committed
Fix list concatenation not using outer list type context
1 parent 0cd1541 commit b30a94f

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

mypy/checkexpr.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3608,6 +3608,12 @@ def visit_op_expr(self, e: OpExpr) -> Type:
36083608
if e.op == "*" and isinstance(e.left, ListExpr):
36093609
# Expressions of form [...] * e get special type inference.
36103610
return self.check_list_multiply(e)
3611+
if e.op == "+":
3612+
# Expressions of form list + list under a list type context get special
3613+
# type inference so literals/comprehensions honor the outer context.
3614+
ctx = get_proper_type(self.type_context[-1])
3615+
if is_named_instance(ctx, "builtins.list"):
3616+
return self.check_list_add(e)
36113617
if e.op == "%":
36123618
if isinstance(e.left, BytesExpr):
36133619
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
@@ -4505,6 +4511,23 @@ def check_list_multiply(self, e: OpExpr) -> Type:
45054511
e.method_type = method_type
45064512
return result
45074513

4514+
def check_list_add(self, e: OpExpr) -> Type:
4515+
"""Type check list concatenation under an outer list type context.
4516+
4517+
Like list literals and '[...] * n', both operands should see the outer
4518+
list[...] context so that e.g. ``x: list[int | None] = [0] + [1]`` works.
4519+
"""
4520+
ctx = self.type_context[-1]
4521+
left_type = self.accept(e.left, type_context=ctx)
4522+
right_type = self.accept(e.right, type_context=ctx)
4523+
# check_op re-accepts the right operand; keep the context-aware type.
4524+
with self.type_overrides_set([e.right], [right_type]):
4525+
result, method_type = self.check_op(
4526+
"__add__", left_type, e.right, e, allow_reverse=True
4527+
)
4528+
e.method_type = method_type
4529+
return result
4530+
45084531
def visit_assignment_expr(self, e: AssignmentExpr) -> Type:
45094532
value = self.accept(e.value)
45104533
binder_version = self.chk.binder.version

test-data/unit/check-inference-context.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,18 @@ if int():
849849
a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int"
850850
[builtins fixtures/list.pyi]
851851

852+
[case testListAddInContext]
853+
from typing import List, Optional
854+
x: List[Optional[int]]
855+
x = [0, 1, 2] + [10]
856+
x = [i for i in [0, 1, 2]] + [10]
857+
x = [0] + [None]
858+
x = [] + [1]
859+
x = [1] + [2] + [3]
860+
y: List[int]
861+
y = [0] + ['x'] # E: List item 0 has incompatible type "str"; expected "int"
862+
[builtins fixtures/list.pyi]
863+
852864
[case testUnionTypeContext]
853865
from typing import Union, List, TypeVar
854866
T = TypeVar('T')

0 commit comments

Comments
 (0)