Skip to content

Commit e88ff4c

Browse files
authored
Point incompatible assignment errors to rvalue instead of lvalue (python#7569)
This seems more logical. If there is a tuple lvalue, we point to the incompatible tuple item. Note that this may break some `# type: ignore` statements since the reported line number may change (but it usually doesn't).
1 parent 7bc9010 commit e88ff4c

File tree

8 files changed

+52
-22
lines changed

8 files changed

+52
-22
lines changed

mypy/checker.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,7 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type
19861986
new_syntax: bool = False) -> None:
19871987
"""Type check a single assignment: lvalue = rvalue."""
19881988
if isinstance(lvalue, TupleExpr) or isinstance(lvalue, ListExpr):
1989-
self.check_assignment_to_multiple_lvalues(lvalue.items, rvalue, lvalue,
1989+
self.check_assignment_to_multiple_lvalues(lvalue.items, rvalue, rvalue,
19901990
infer_lvalue_type)
19911991
else:
19921992
lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue)
@@ -2056,9 +2056,9 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type
20562056
lvalue.kind is None): # Ignore member access to modules
20572057
instance_type = self.expr_checker.accept(lvalue.expr)
20582058
rvalue_type, lvalue_type, infer_lvalue_type = self.check_member_assignment(
2059-
instance_type, lvalue_type, rvalue, lvalue)
2059+
instance_type, lvalue_type, rvalue, context=rvalue)
20602060
else:
2061-
rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, lvalue,
2061+
rvalue_type = self.check_simple_assignment(lvalue_type, rvalue, context=rvalue,
20622062
code=codes.ASSIGNMENT)
20632063

20642064
# Special case: only non-abstract non-protocol classes can be assigned to
@@ -2196,7 +2196,7 @@ def check_compatibility_super(self, lvalue: RefExpr, lvalue_type: Optional[Type]
21962196
if base_static and compare_static:
21972197
lvalue_node.is_staticmethod = True
21982198

2199-
return self.check_subtype(compare_type, base_type, lvalue,
2199+
return self.check_subtype(compare_type, base_type, rvalue,
22002200
message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT,
22012201
'expression has type',
22022202
'base class "%s" defined the type as' % base.name(),

mypy/fastparse.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,14 @@ def visit_Assign(self, n: ast3.Assign) -> AssignmentStmt:
729729

730730
# AnnAssign(expr target, expr annotation, expr? value, int simple)
731731
def visit_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt:
732+
line = n.lineno
732733
if n.value is None: # always allow 'x: int'
733734
rvalue = TempNode(AnyType(TypeOfAny.special_form), no_rhs=True) # type: Expression
735+
rvalue.line = line
736+
rvalue.column = n.col_offset
734737
else:
735738
rvalue = self.visit(n.value)
736-
typ = TypeConverter(self.errors, line=n.lineno).visit(n.annotation)
739+
typ = TypeConverter(self.errors, line=line).visit(n.annotation)
737740
assert typ is not None
738741
typ.column = n.annotation.col_offset
739742
s = AssignmentStmt([self.visit(n.target)], rvalue, type=typ, new_syntax=True)

test-data/unit/check-columns.test

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,44 @@ y = 'hello'
8181
if int():
8282
x = 2; y = x; y += 1
8383
[out]
84-
main:4:12: error: Incompatible types in assignment (expression has type "int", variable has type "str")
84+
main:4:16: error: Incompatible types in assignment (expression has type "int", variable has type "str")
8585
main:4:24: error: Unsupported operand types for + ("str" and "int")
8686

87+
[case testColumnsAssignment]
88+
class A:
89+
x = 0
90+
91+
A().x = '' # E:9: Incompatible types in assignment (expression has type "str", variable has type "int")
92+
a = [0]
93+
a[0] = '' # E:8: Incompatible types in assignment (expression has type "str", target has type "int")
94+
b = 0
95+
c = 0
96+
b, c = 0, '' # E:11: Incompatible types in assignment (expression has type "str", variable has type "int")
97+
b, c = '', 0 # E:8: Incompatible types in assignment (expression has type "str", variable has type "int")
98+
99+
t = 0, ''
100+
b, c = t # E:8: Incompatible types in assignment (expression has type "str", variable has type "int")
101+
102+
class B(A):
103+
x = '' # E:9: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int")
104+
[builtins fixtures/list.pyi]
105+
106+
[case testColumnsAttributeIncompatibleWithBaseClassUsingAnnotation]
107+
class A:
108+
x: str
109+
110+
class B(A):
111+
x: int # E:5: Incompatible types in assignment (expression has type "int", base class "A" defined the type as "str")
112+
87113
[case testColumnsSimpleIsinstance]
88114
import typing
89115
def f(x: object, n: int, s: str) -> None:
90116
if int():
91-
n = x # E:9: Incompatible types in assignment (expression has type "object", variable has type "int")
117+
n = x # E:13: Incompatible types in assignment (expression has type "object", variable has type "int")
92118
if isinstance(x, int):
93119
n = x
94-
s = x # E:13: Incompatible types in assignment (expression has type "int", variable has type "str")
95-
n = x # E:9: Incompatible types in assignment (expression has type "object", variable has type "int")
120+
s = x # E:17: Incompatible types in assignment (expression has type "int", variable has type "str")
121+
n = x # E:13: Incompatible types in assignment (expression has type "object", variable has type "int")
96122
[builtins fixtures/isinstance.pyi]
97123

98124
[case testColumnHasNoAttribute]
@@ -247,9 +273,9 @@ class C:
247273

248274
p: P
249275
if int():
250-
p = C() # E:5: Incompatible types in assignment (expression has type "C", variable has type "P") \
251-
# N:5: 'C' is missing following 'P' protocol member: \
252-
# N:5: y
276+
p = C() # E:9: Incompatible types in assignment (expression has type "C", variable has type "P") \
277+
# N:9: 'C' is missing following 'P' protocol member: \
278+
# N:9: y
253279

254280
[case testColumnRedundantCast]
255281
# flags: --warn-redundant-casts
@@ -354,6 +380,6 @@ def f(x: T) -> T:
354380
# XXX: Disabled because the column differs in 3.8
355381
# aa: List[int] = ['' for x in [1]] # :22: List comprehension has incompatible type List[str]; expected List[int]
356382
cc = (1).bad # E:11: "int" has no attribute "bad"
357-
n: int = '' # E:5: Incompatible types in assignment (expression has type "str", variable has type "int")
383+
n: int = '' # E:14: Incompatible types in assignment (expression has type "str", variable has type "int")
358384
return x
359385
[builtins fixtures/list.pyi]

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,10 @@ f = lambda x: A() # type: Callable[[], A]
656656
f2 = lambda: A() # type: Callable[[A], A]
657657
class A: pass
658658
[out]
659-
main:2: error: Incompatible types in assignment (expression has type "Callable[[Any], A]", variable has type "Callable[[], A]")
660659
main:2: error: Cannot infer type of lambda
661-
main:3: error: Incompatible types in assignment (expression has type "Callable[[], A]", variable has type "Callable[[A], A]")
660+
main:2: error: Incompatible types in assignment (expression has type "Callable[[Any], A]", variable has type "Callable[[], A]")
662661
main:3: error: Cannot infer type of lambda
662+
main:3: error: Incompatible types in assignment (expression has type "Callable[[], A]", variable has type "Callable[[A], A]")
663663

664664
[case testEllipsisContextForLambda]
665665
from typing import Callable

test-data/unit/check-literal.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,8 +2582,9 @@ class Foo:
25822582
self.instancevar3: Final[Literal[False]] = True # E: Incompatible types in assignment (expression has type "Literal[True]", variable has type "Literal[False]")
25832583

25842584
# TODO: Fix the order in which these error messages are shown to be more consistent.
2585-
var1 = 10 # E: Incompatible types in assignment (expression has type "Literal[10]", variable has type "Literal[4]") \
2586-
# E: Cannot assign to final name "var1"
2585+
var1 = 10 # E: Cannot assign to final name "var1" \
2586+
# E: Incompatible types in assignment (expression has type "Literal[10]", variable has type "Literal[4]")
2587+
25872588

25882589
Foo.classvar1 = 10 # E: Cannot assign to final attribute "classvar1" \
25892590
# E: Incompatible types in assignment (expression has type "Literal[10]", variable has type "Literal[4]")

test-data/unit/cmdline.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,8 +1408,8 @@ some_file.py:3: error: Unsupported operand types for + ("int" and "str")
14081408
^
14091409
some_file.py:11: error: Incompatible types in assignment (expression has type
14101410
"AnotherCustomClassDefinedBelow", variable has type "OneCustomClassName")
1411-
self.very_important_attribute_with_long_name: OneCustomClassNa...
1412-
^
1411+
...t_attribute_with_long_name: OneCustomClassName = OneCustomClassName()....
1412+
^
14131413
some_file.py:11: error: Argument 1 to "some_interesting_method" of
14141414
"OneCustomClassName" has incompatible type "Union[int, str, float]"; expected
14151415
"AnotherCustomClassDefinedBelow"

test-data/unit/semanal-typeddict.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ MypyFile:1(
5252
StrExpr(foo))
5353
AssignmentStmt:4(
5454
NameExpr(x)
55-
TempNode:-1(
55+
TempNode:4(
5656
Any)
5757
str?)))

test-data/unit/semenal-literal.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ MypyFile:1(
66
ImportFrom:1(typing_extensions, [Literal])
77
AssignmentStmt:2(
88
NameExpr(foo [__main__.foo])
9-
TempNode:-1(
9+
TempNode:2(
1010
Any)
1111
Literal[3]))
1212

13-
[case testLiteralSemenalInFunction]
13+
[case testLiteralSemanalInFunction]
1414
from typing_extensions import Literal
1515
def foo(a: Literal[1], b: Literal[" foo "]) -> Literal[True]: pass
1616
[builtins fixtures/bool.pyi]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy