diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9b430529033c..1cc785f5064f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -178,6 +178,19 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: e.callee.node.typeddict_type is not None: return self.check_typeddict_call(e.callee.node.typeddict_type, e.arg_kinds, e.arg_names, e.args, e) + if isinstance(e.callee, NameExpr) and e.callee.name in ('isinstance', 'issubclass'): + for typ in mypy.checker.flatten(e.args[1]): + if isinstance(typ, NameExpr): + try: + node = self.chk.lookup_qualified(typ.name) + except KeyError: + # Undefined names should already be reported in semantic analysis. + node = None + if (isinstance(typ, IndexExpr) + and isinstance(typ.analyzed, (TypeApplication, TypeAliasExpr)) + # node.kind == TYPE_ALIAS only for aliases like It = Iterable[int]. + or isinstance(typ, NameExpr) and node and node.kind == nodes.TYPE_ALIAS): + self.msg.type_arguments_not_allowed(e) self.try_infer_partial_type(e) callee_type = self.accept(e.callee) if (self.chk.options.disallow_untyped_calls and diff --git a/mypy/messages.py b/mypy/messages.py index b327347d2c22..78e17686d057 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -881,6 +881,9 @@ def typeddict_item_name_not_found(self, self.fail('\'{}\' is not a valid item name; expected one of {}'.format( item_name, format_item_name_list(typ.items.keys())), context) + def type_arguments_not_allowed(self, context: Context) -> None: + self.fail('Parameterized generics cannot be used with class or instance checks', context) + def capitalize(s: str) -> str: """Capitalize the first character of a string.""" diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index af06f6205e45..63874eb4f9ba 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1423,6 +1423,45 @@ def f(x: Union[int, A], a: Type[A]) -> None: [builtins fixtures/isinstancelist.pyi] +[case testIsinstanceTypeArgs] +from typing import Iterable, TypeVar +x = 1 +T = TypeVar('T') + +isinstance(x, Iterable) +isinstance(x, Iterable[int]) # E: Parameterized generics cannot be used with class or instance checks +isinstance(x, Iterable[T]) # E: Parameterized generics cannot be used with class or instance checks +isinstance(x, (int, Iterable[int])) # E: Parameterized generics cannot be used with class or instance checks +isinstance(x, (int, (str, Iterable[int]))) # E: Parameterized generics cannot be used with class or instance checks + +[builtins fixtures/isinstancelist.pyi] + +[case testIsinstanceTypeArgsAliases] +from typing import Iterable, TypeVar +x = 1 +T = TypeVar('T') +It = Iterable +It2 = Iterable[T] + +isinstance(x, It[int]) # E: Parameterized generics cannot be used with class or instance checks +isinstance(x, It) +isinstance(x, It2[int]) # E: Parameterized generics cannot be used with class or instance checks +isinstance(x, It2) # E: Parameterized generics cannot be used with class or instance checks + +[builtins fixtures/isinstance.pyi] + + +[case testIssubclassTypeArgs] +from typing import Iterable, TypeVar +x = int +T = TypeVar('T') +issubclass(x, Iterable) +issubclass(x, Iterable[int]) # E: Parameterized generics cannot be used with class or instance checks +issubclass(x, Iterable[T]) # E: Parameterized generics cannot be used with class or instance checks +issubclass(x, (int, Iterable[int])) # E: Parameterized generics cannot be used with class or instance checks + +[builtins fixtures/isinstance.pyi] + [case testIsinstanceAndNarrowTypeVariable] from typing import TypeVar diff --git a/test-data/unit/fixtures/isinstance.pyi b/test-data/unit/fixtures/isinstance.pyi index 9d87a161395f..7ee7aa5d31f8 100644 --- a/test-data/unit/fixtures/isinstance.pyi +++ b/test-data/unit/fixtures/isinstance.pyi @@ -13,6 +13,7 @@ class tuple(Generic[T]): pass class function: pass def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass +def issubclass(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass class int: def __add__(self, other: 'int') -> 'int': pass