diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index d17974697b47..f6489ae4a98e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -250,6 +250,11 @@ def check_call(self, callee: Type, args: List[Node], self.check_argument_types(arg_types, arg_kinds, callee, formal_to_actual, context, messages=arg_messages) + + if (callee.is_type_obj() and (len(arg_types) == 1) + and is_equivalent(callee.ret_type, self.named_type('builtins.type'))): + callee = callee.copy_modified(ret_type=TypeType(arg_types[0])) + if callable_node: # Store the inferred callable type. self.chk.store_type(callable_node, callee) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 737732dab7b4..513478259d54 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -1818,8 +1818,10 @@ def f(a: Type[User]) -> None: pass @overload def f(a: int) -> None: pass +def mock() -> type: return User + f(User) -f(type(User)) # E: No overload variant of "f" matches argument types [builtins.type] +f(mock()) # E: No overload variant of "f" matches argument types [builtins.type] [builtins fixtures/classmethod.py] [out] @@ -1948,3 +1950,59 @@ def f(a: object) -> str: pass [builtins fixtures/classmethod.py] [out] +[case testTypeConstructorReturnsTypeType] +class User: + @classmethod + def test_class_method(cls) -> int: pass + @staticmethod + def test_static_method() -> str: pass + def test_instance_method(self) -> None: pass + +u = User() + +reveal_type(type(u)) # E: Revealed type is 'Type[__main__.User]' +reveal_type(type(u).test_class_method()) # E: Revealed type is 'builtins.int' +reveal_type(type(u).test_static_method()) # E: Revealed type is 'builtins.str' +type(u).test_instance_method() # E: Too few arguments for "test_instance_method" of "User" +[builtins fixtures/classmethod.py] +[out] + +[case testObfuscatedTypeConstructorReturnsTypeType] +from typing import TypeVar +class User: pass + +f1 = type + +A = TypeVar('A') +def f2(func: A) -> A: + return func + +u = User() + +reveal_type(f1(u)) # E: Revealed type is 'Type[__main__.User]' +reveal_type(f2(type)(u)) # E: Revealed type is 'Type[__main__.User]' +[builtins fixtures/classmethod.py] +[out] + +[case testTypeConstructorLookalikeFails] +class User: pass + +def fake1(a: object) -> type: + return User +def fake2(a: int) -> type: + return User + +reveal_type(type(User())) # E: Revealed type is 'Type[__main__.User]' +reveal_type(fake1(User())) # E: Revealed type is 'builtins.type' +reveal_type(fake2(3)) # E: Revealed type is 'builtins.type' +[builtins fixtures/classmethod.py] +[out] + +[case testOtherTypeConstructorsSucceed] +def foo(self) -> int: return self.attr + +User = type('User', (object,), {'foo': foo, 'attr': 3}) +reveal_type(User) # E: Revealed type is 'builtins.type' +[builtins fixtures/args.py] +[out] + diff --git a/test-data/unit/fixtures/args.py b/test-data/unit/fixtures/args.py index 9fb62b94b719..dd7d9ea61c6f 100644 --- a/test-data/unit/fixtures/args.py +++ b/test-data/unit/fixtures/args.py @@ -1,6 +1,6 @@ # Builtins stub used to support *args, **kwargs. -from typing import TypeVar, Generic, Iterable +from typing import TypeVar, Generic, Iterable, Tuple, Dict, Any, overload Tco = TypeVar('Tco', covariant=True) T = TypeVar('T') @@ -9,7 +9,12 @@ class object: def __init__(self) -> None: pass -class type: pass +class type: + @overload + def __init__(self, o: object) -> None: pass + @overload + def __init__(self, name: str, bases: Tuple[type, ...], dict: Dict[str, Any]) -> None: pass + class tuple(Iterable[Tco], Generic[Tco]): pass class dict(Generic[T, S]): pass diff --git a/test-data/unit/fixtures/classmethod.py b/test-data/unit/fixtures/classmethod.py index 98cff8a15e2a..f6333bd7b289 100644 --- a/test-data/unit/fixtures/classmethod.py +++ b/test-data/unit/fixtures/classmethod.py @@ -9,7 +9,9 @@ def mro(self) -> typing.Any: pass class function: pass -classmethod = object() # Dummy definition. +# Dummy definitions. +classmethod = object() +staticmethod = object() class int: @classmethod