diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 6c934dccb022..1a22602b8b02 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -632,11 +632,14 @@ def expand(target: Type) -> Type: arg_types = func.arg_types[1:] ret_type = func.ret_type variables = func.variables + if isinstance(original_type, CallableType) and original_type.is_type_obj(): + original_type = TypeType(original_type.ret_type) res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, - ret_type=ret_type) + ret_type=ret_type, + bound_args=[original_type]) return cast(F, res) diff --git a/mypy/messages.py b/mypy/messages.py index da1b2bfb4c04..b327347d2c22 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -485,7 +485,10 @@ def incompatible_argument(self, n: int, m: int, callee: CallableType, arg_type: target = '' if callee.name: name = callee.name - base = extract_type(name) + if callee.bound_args and callee.bound_args[0] is not None: + base = self.format(callee.bound_args[0]) + else: + base = extract_type(name) for op, method in op_methods.items(): for variant in method, '__r' + method[2:]: diff --git a/mypy/types.py b/mypy/types.py index 1bba37724ced..9a1b1838e9cf 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -534,6 +534,8 @@ class CallableType(FunctionLike): # Was this callable generated by analyzing Type[...] instantiation? from_type_type = False # type: bool + bound_args = None # type: List[Type] + def __init__(self, arg_types: List[Type], arg_kinds: List[int], @@ -550,6 +552,7 @@ def __init__(self, is_classmethod_class: bool = False, special_sig: Optional[str] = None, from_type_type: bool = False, + bound_args: List[Type] = None, ) -> None: if variables is None: variables = [] @@ -571,6 +574,7 @@ def __init__(self, self.is_classmethod_class = is_classmethod_class self.special_sig = special_sig self.from_type_type = from_type_type + self.bound_args = bound_args or [] super().__init__(line, column) def copy_modified(self, @@ -586,7 +590,8 @@ def copy_modified(self, column: int = _dummy, is_ellipsis_args: bool = _dummy, special_sig: Optional[str] = _dummy, - from_type_type: bool = _dummy) -> 'CallableType': + from_type_type: bool = _dummy, + bound_args: List[Type] = _dummy) -> 'CallableType': return CallableType( arg_types=arg_types if arg_types is not _dummy else self.arg_types, arg_kinds=arg_kinds if arg_kinds is not _dummy else self.arg_kinds, @@ -604,6 +609,7 @@ def copy_modified(self, is_classmethod_class=self.is_classmethod_class, special_sig=special_sig if special_sig is not _dummy else self.special_sig, from_type_type=from_type_type if from_type_type is not _dummy else self.from_type_type, + bound_args=bound_args if bound_args is not _dummy else self.bound_args, ) def is_type_obj(self) -> bool: @@ -739,6 +745,8 @@ def serialize(self) -> JsonDict: 'is_ellipsis_args': self.is_ellipsis_args, 'implicit': self.implicit, 'is_classmethod_class': self.is_classmethod_class, + 'bound_args': [(None if t is None else t.serialize()) + for t in self.bound_args], } @classmethod @@ -756,6 +764,8 @@ def deserialize(cls, data: JsonDict) -> 'CallableType': is_ellipsis_args=data['is_ellipsis_args'], implicit=data['implicit'], is_classmethod_class=data['is_classmethod_class'], + bound_args=[(None if t is None else deserialize_type(t)) + for t in data['bound_args']], ) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 56893383b4fd..6cb42d70eb86 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3038,7 +3038,7 @@ class Concrete(metaclass=Meta): pass reveal_type(Concrete + X()) # E: Revealed type is 'builtins.str' -Concrete + "hello" # E: Unsupported operand types for + ("Meta" and "str") +Concrete + "hello" # E: Unsupported operand types for + (Type[Concrete] and "str") [case testMetaclassGetitem] class M(type): diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 64d010e313fd..9d1a0ae8899e 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -284,9 +284,9 @@ class B: pass class C: pass [out] main:8: error: Incompatible types in assignment (expression has type "C", variable has type "B") -main:9: error: Unsupported operand types for + ("A" and "C") +main:9: error: Unsupported operand types for + (A[B, C] and "C") main:10: error: Incompatible types in assignment (expression has type "B", variable has type "C") -main:11: error: Invalid index type "B" for "A"; expected type "C" +main:11: error: Invalid index type "B" for A[B, C]; expected type "C" [case testOperatorAssignmentWithIndexLvalue1] from typing import TypeVar, Generic @@ -310,7 +310,7 @@ class C: [out] main:7: error: Unsupported operand types for + ("C" and "B") main:7: error: Incompatible types in assignment (expression has type "B", target has type "C") -main:8: error: Invalid index type "C" for "A"; expected type "B" +main:8: error: Invalid index type "C" for A[C]; expected type "B" [case testOperatorAssignmentWithIndexLvalue2] from typing import TypeVar, Generic @@ -331,9 +331,9 @@ class B: pass class C: def __add__(self, o: 'C') -> 'C': pass [out] -main:7: error: Invalid index type "B" for "A"; expected type "C" -main:8: error: Invalid index type "C" for "A"; expected type "B" -main:9: error: Invalid index type "B" for "A"; expected type "C" +main:7: error: Invalid index type "B" for A[C]; expected type "C" +main:8: error: Invalid index type "C" for A[C]; expected type "B" +main:9: error: Invalid index type "B" for A[C]; expected type "C" -- Nested generic types diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index fb6f88871a30..af06f6205e45 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -679,7 +679,7 @@ while bool(): x + 'a' break x + [1] - x + 'a' # E: Unsupported operand types for + ("list" and "str") + x + 'a' # E: Unsupported operand types for + (List[int] and "str") x + [1] # E: Unsupported operand types for + (likely involving Union) [builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index 25fc499e37e3..9f3f87853e63 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -29,7 +29,7 @@ from typing import Dict, Any d: Dict[int, str] = {} d[42] = 'ab' d[42] = 42 # E: Incompatible types in assignment (expression has type "int", target has type "str") -d['ab'] = 'ab' # E: Invalid index type "str" for "dict"; expected type "int" +d['ab'] = 'ab' # E: Invalid index type "str" for Dict[int, str]; expected type "int" [builtins fixtures/dict.pyi] [out] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index af0f28d74157..e4fb2a16025c 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -196,7 +196,7 @@ x = {None: None} x["bar"] = 1 [builtins fixtures/dict.pyi] [out] -main:2: error: Invalid index type "str" for "dict"; expected type None +main:2: error: Invalid index type "str" for Dict[None, None]; expected type None main:2: error: Incompatible types in assignment (expression has type "int", target has type None) [case testInferNonOptionalDictType] diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index d1c2b314019e..567b9a836812 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -577,7 +577,7 @@ print(tuple(a)) import typing [1] + iter([2, 3]) [out] -_program.py:2: error: Unsupported operand types for + ("list" and Iterator[int]) +_program.py:2: error: Unsupported operand types for + (List[int] and Iterator[int]) [case testInferHeterogeneousListOfIterables] from typing import Sequence @@ -1095,10 +1095,10 @@ MyDDict(dict)['0'] MyDDict(dict)[0] [out] _program.py:6: error: Argument 1 to "defaultdict" has incompatible type List[_T]; expected Callable[[], str] -_program.py:9: error: Invalid index type "str" for "dict"; expected type "int" +_program.py:9: error: Invalid index type "str" for defaultdict[int, str]; expected type "int" _program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str") _program.py:19: error: Dict entry 0 has incompatible type "str": List[] -_program.py:23: error: Invalid index type "str" for "dict"; expected type "int" +_program.py:23: error: Invalid index type "str" for MyDDict[Dict[_KT, _VT]]; expected type "int" [case testNoSubcriptionOfStdlibCollections] import collections @@ -1120,7 +1120,7 @@ def f(d: collections.defaultdict[int, str]) -> None: _program.py:5: error: "defaultdict" is not subscriptable _program.py:6: error: "Counter" is not subscriptable _program.py:9: error: "defaultdict" is not subscriptable -_program.py:12: error: Invalid index type "int" for "dict"; expected type "str" +_program.py:12: error: Invalid index type "int" for defaultdict[str, int]; expected type "str" _program.py:14: error: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead [case testCollectionsAliases] @@ -1148,7 +1148,7 @@ reveal_type(o6) [out] _testCollectionsAliases.py:5: error: Revealed type is 'collections.Counter[builtins.int]' -_testCollectionsAliases.py:6: error: Invalid index type "str" for "dict"; expected type "int" +_testCollectionsAliases.py:6: error: Invalid index type "str" for Counter[int]; expected type "int" _testCollectionsAliases.py:9: error: Revealed type is 'collections.ChainMap[builtins.int, builtins.str]' _testCollectionsAliases.py:12: error: Revealed type is 'collections.deque[builtins.int]' _testCollectionsAliases.py:15: error: Revealed type is 'collections.Counter[builtins.int*]'