Skip to content

Commit 1ea1122

Browse files
Felipe de Moraisilevkivskyi
Felipe de Morais
authored andcommitted
Better error message for assignments and returns incompatible due to invariance (#6803)
Fixes: #4186
1 parent 9c91487 commit 1ea1122

7 files changed

+67
-12
lines changed

mypy/checker.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
is_optional, remove_optional
3737
)
3838
from mypy.sametypes import is_same_type
39-
from mypy.messages import MessageBuilder, make_inferred_type_note
39+
from mypy.messages import MessageBuilder, make_inferred_type_note, append_invariance_notes
4040
import mypy.checkexpr
4141
from mypy.checkmember import (
4242
map_type_from_supertype, bind_self, erase_to_bound, type_object_type,
@@ -3569,6 +3569,7 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context,
35693569
return False
35703570
extra_info = [] # type: List[str]
35713571
note_msg = ''
3572+
notes = [] # type: List[str]
35723573
if subtype_label is not None or supertype_label is not None:
35733574
subtype_str, supertype_str = self.msg.format_distinctly(subtype, supertype)
35743575
if subtype_label is not None:
@@ -3577,9 +3578,13 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context,
35773578
extra_info.append(supertype_label + ' ' + supertype_str)
35783579
note_msg = make_inferred_type_note(context, subtype,
35793580
supertype, supertype_str)
3581+
if isinstance(subtype, Instance) and isinstance(supertype, Instance):
3582+
notes = append_invariance_notes([], subtype, supertype)
35803583
if extra_info:
35813584
msg += ' (' + ', '.join(extra_info) + ')'
35823585
self.fail(msg, context)
3586+
for note in notes:
3587+
self.msg.note(note, context)
35833588
if note_msg:
35843589
self.note(note_msg, context)
35853590
if (isinstance(supertype, Instance) and supertype.type.is_protocol and

test-data/unit/check-basic.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,21 @@ none = None
372372
b = none.__bool__()
373373
reveal_type(b) # E: Revealed type is 'builtins.bool'
374374
[builtins fixtures/bool.pyi]
375+
376+
[case testAssignmentInvariantNoteForList]
377+
from typing import List
378+
x: List[int]
379+
y: List[float]
380+
y = x # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") \
381+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
382+
# N: Consider using "Sequence" instead, which is covariant
383+
[builtins fixtures/list.pyi]
384+
385+
[case testAssignmentInvariantNoteForDict]
386+
from typing import Dict
387+
x: Dict[str, int]
388+
y: Dict[str, float]
389+
y = x # E: Incompatible types in assignment (expression has type "Dict[str, int]", variable has type "Dict[str, float]") \
390+
# N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
391+
# N: Consider using "Mapping" instead, which is covariant in the value type
392+
[builtins fixtures/dict.pyi]

test-data/unit/check-functions.test

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,12 +2364,16 @@ from typing import Union, Dict, List
23642364
def f() -> List[Union[str, int]]:
23652365
x = ['a']
23662366
return x # E: Incompatible return value type (got "List[str]", expected "List[Union[str, int]]") \
2367-
# N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]"
2367+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
2368+
# N: Consider using "Sequence" instead, which is covariant \
2369+
# N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]"
23682370

23692371
def g() -> Dict[str, Union[str, int]]:
23702372
x = {'a': 'a'}
23712373
return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[str, Union[str, int]]") \
2372-
# N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]"
2374+
# N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
2375+
# N: Consider using "Mapping" instead, which is covariant in the value type \
2376+
# N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]"
23732377

23742378
def h() -> Dict[Union[str, int], str]:
23752379
x = {'a': 'a'}
@@ -2378,7 +2382,9 @@ def h() -> Dict[Union[str, int], str]:
23782382

23792383
def i() -> List[Union[int, float]]:
23802384
x: List[int] = [1]
2381-
return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]")
2385+
return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") \
2386+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
2387+
# N: Consider using "Sequence" instead, which is covariant
23822388

23832389
[builtins fixtures/dict.pyi]
23842390

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,10 @@ T = TypeVar('T', bound=int)
13201320
def f(x: List[T]) -> List[T]: ...
13211321

13221322
# TODO: improve error message for such cases, see #3283 and #5706
1323-
y: List[str] = f([]) # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[str]")
1323+
y: List[str] = f([]) \
1324+
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[str]") \
1325+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
1326+
# N: Consider using "Sequence" instead, which is covariant
13241327
[builtins fixtures/list.pyi]
13251328

13261329
[case testWideOuterContextNoArgs]
@@ -1339,7 +1342,10 @@ from typing import TypeVar, Optional, List
13391342
T = TypeVar('T', bound=int)
13401343
def f(x: Optional[T] = None) -> List[T]: ...
13411344

1342-
y: List[str] = f() # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[str]")
1345+
y: List[str] = f() \
1346+
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[str]") \
1347+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
1348+
# N: Consider using "Sequence" instead, which is covariant
13431349
[builtins fixtures/list.pyi]
13441350

13451351
[case testUseCovariantGenericOuterContext]

test-data/unit/check-inference.test

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,10 @@ if int():
13311331
if int():
13321332
a = x2
13331333
if int():
1334-
a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]")
1334+
a = x3 \
1335+
# E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \
1336+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
1337+
# N: Consider using "Sequence" instead, which is covariant
13351338
[builtins fixtures/list.pyi]
13361339
[typing fixtures/typing-full.pyi]
13371340

@@ -1351,7 +1354,10 @@ if int():
13511354
if int():
13521355
a = x2
13531356
if int():
1354-
a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]")
1357+
a = x3 \
1358+
# E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \
1359+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
1360+
# N: Consider using "Sequence" instead, which is covariant
13551361
[builtins fixtures/list.pyi]
13561362
[typing fixtures/typing-full.pyi]
13571363

test-data/unit/check-typevar-values.test

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ o = [object()]
1919
if int():
2020
i = f(1)
2121
s = f('')
22-
o = f(1) # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]")
22+
o = f(1) \
23+
# E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") \
24+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
25+
# N: Consider using "Sequence" instead, which is covariant
2326
[builtins fixtures/list.pyi]
2427

2528
[case testCallGenericFunctionWithTypeVarValueRestrictionAndAnyArgs]

test-data/unit/check-varargs.test

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,18 +586,29 @@ ab = None # type: List[B]
586586
if int():
587587
a, aa = G().f(*[a]) \
588588
# E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") \
589-
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[A]")
589+
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[A]") \
590+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
591+
# N: Consider using "Sequence" instead, which is covariant
592+
590593
if int():
591594
aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "A")
592595
if int():
593596
ab, aa = G().f(*[a]) \
594597
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[A]") \
598+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
599+
# N: Consider using "Sequence" instead, which is covariant \
595600
# E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B"
596601

597602
if int():
598-
ao, ao = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[object]")
603+
ao, ao = G().f(*[a]) \
604+
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[object]") \
605+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
606+
# N: Consider using "Sequence" instead, which is covariant
599607
if int():
600-
aa, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[A]")
608+
aa, aa = G().f(*[a]) \
609+
# E: Incompatible types in assignment (expression has type "List[<nothing>]", variable has type "List[A]") \
610+
# N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \
611+
# N: Consider using "Sequence" instead, which is covariant
601612

602613
class G(Generic[T]):
603614
def f(self, *a: S) -> Tuple[List[S], List[T]]:

0 commit comments

Comments
 (0)