diff --git a/mypy/checker.py b/mypy/checker.py index e25368a19d0c..ccbab33b7b96 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -36,7 +36,7 @@ is_optional, remove_optional ) from mypy.sametypes import is_same_type -from mypy.messages import MessageBuilder, make_inferred_type_note +from mypy.messages import MessageBuilder, make_inferred_type_note, append_invariance_notes import mypy.checkexpr from mypy.checkmember import ( 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, return False extra_info = [] # type: List[str] note_msg = '' + notes = [] # type: List[str] if subtype_label is not None or supertype_label is not None: subtype_str, supertype_str = self.msg.format_distinctly(subtype, supertype) if subtype_label is not None: @@ -3577,9 +3578,13 @@ def check_subtype(self, subtype: Type, supertype: Type, context: Context, extra_info.append(supertype_label + ' ' + supertype_str) note_msg = make_inferred_type_note(context, subtype, supertype, supertype_str) + if isinstance(subtype, Instance) and isinstance(supertype, Instance): + notes = append_invariance_notes([], subtype, supertype) if extra_info: msg += ' (' + ', '.join(extra_info) + ')' self.fail(msg, context) + for note in notes: + self.msg.note(note, context) if note_msg: self.note(note_msg, context) if (isinstance(supertype, Instance) and supertype.type.is_protocol and diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 629d6bf291d2..53797ada44e8 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -372,3 +372,21 @@ none = None b = none.__bool__() reveal_type(b) # E: Revealed type is 'builtins.bool' [builtins fixtures/bool.pyi] + +[case testAssignmentInvariantNoteForList] +from typing import List +x: List[int] +y: List[float] +y = x # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant +[builtins fixtures/list.pyi] + +[case testAssignmentInvariantNoteForDict] +from typing import Dict +x: Dict[str, int] +y: Dict[str, float] +y = x # E: Incompatible types in assignment (expression has type "Dict[str, int]", variable has type "Dict[str, float]") \ + # N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Mapping" instead, which is covariant in the value type +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 0114e3d10eab..9bcc09e28d8d 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2364,12 +2364,16 @@ from typing import Union, Dict, List def f() -> List[Union[str, int]]: x = ['a'] return x # E: Incompatible return value type (got "List[str]", expected "List[Union[str, int]]") \ -# N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]" + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant \ + # N: Perhaps you need a type annotation for "x"? Suggestion: "List[Union[str, int]]" def g() -> Dict[str, Union[str, int]]: x = {'a': 'a'} return x # E: Incompatible return value type (got "Dict[str, str]", expected "Dict[str, Union[str, int]]") \ -# N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]" + # N: "Dict" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Mapping" instead, which is covariant in the value type \ + # N: Perhaps you need a type annotation for "x"? Suggestion: "Dict[str, Union[str, int]]" def h() -> Dict[Union[str, int], str]: x = {'a': 'a'} @@ -2378,7 +2382,9 @@ def h() -> Dict[Union[str, int], str]: def i() -> List[Union[int, float]]: x: List[int] = [1] - return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") + return x # E: Incompatible return value type (got "List[int]", expected "List[Union[int, float]]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index af3abab0ce23..58d561ffafb7 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -1320,7 +1320,10 @@ T = TypeVar('T', bound=int) def f(x: List[T]) -> List[T]: ... # TODO: improve error message for such cases, see #3283 and #5706 -y: List[str] = f([]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") +y: List[str] = f([]) \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [case testWideOuterContextNoArgs] @@ -1339,7 +1342,10 @@ from typing import TypeVar, Optional, List T = TypeVar('T', bound=int) def f(x: Optional[T] = None) -> List[T]: ... -y: List[str] = f() # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") +y: List[str] = f() \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[str]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [case testUseCovariantGenericOuterContext] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 656c1b67f5ad..9d7887d05ae9 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1331,7 +1331,10 @@ if int(): if int(): a = x2 if int(): - a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") + a = x3 \ + # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] @@ -1351,7 +1354,10 @@ if int(): if int(): a = x2 if int(): - a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") + a = x3 \ + # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 9e9056845dfc..69a54dc34bd9 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -19,7 +19,10 @@ o = [object()] if int(): i = f(1) s = f('') - o = f(1) # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") + o = f(1) \ + # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[object]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant [builtins fixtures/list.pyi] [case testCallGenericFunctionWithTypeVarValueRestrictionAndAnyArgs] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 357b0ed63a62..cad333eb2746 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -586,18 +586,29 @@ ab = None # type: List[B] if int(): a, aa = G().f(*[a]) \ # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") \ - # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant + if int(): aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") if int(): ab, aa = G().f(*[a]) \ # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant \ # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" if int(): - ao, ao = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[object]") + ao, ao = G().f(*[a]) \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[object]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant if int(): - aa, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") + aa, aa = G().f(*[a]) \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # N: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance \ + # N: Consider using "Sequence" instead, which is covariant class G(Generic[T]): def f(self, *a: S) -> Tuple[List[S], List[T]]: