diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 577576a4e5f8..cfb983f99c37 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1802,10 +1802,22 @@ def check_callable_call( if ( callee.is_type_obj() - and (len(arg_types) == 1) + and len(arg_types) == 1 and is_equivalent(callee.ret_type, self.named_type("builtins.type")) ): - callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) + proper_arg = get_proper_type(arg_types[0]) + if isinstance(proper_arg, Instance) and proper_arg.type.is_protocol: + callee = callee.copy_modified( + ret_type=UnionType( + [ + self.named_type("builtins.type"), + self.named_type("types.ModuleType"), + AnyType(TypeOfAny.special_form), + ] + ) + ) + else: + callee = callee.copy_modified(ret_type=TypeType.make_normalized(arg_types[0])) if callable_node: # Store the inferred callable type. diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 5ed2351e33e6..b9a66cf34792 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -4215,3 +4215,32 @@ def g4(a: Input[bytes], b: Output[str]) -> None: f(a, b) # E: Cannot infer type argument 1 of "f" [builtins fixtures/tuple.pyi] + +[case testTypeCallOnProtocol] +from typing import Any, Protocol, cast +import types + +class P(Protocol): + def foo(self) -> None: ... + +import mod + +a: P = mod +value = type(a) +reveal_type(value) # N: Revealed type is "Union[builtins.type, types.ModuleType, Any]" +reveal_type(value.foo) # E: Item "type" of "Union[type, Module, Any]" has no attribute "foo" \ + # N: Revealed type is "Any" + +class Namespace: ... +n = Namespace() +n.foo = lambda: None # E: "Namespace" has no attribute "foo" + +b: P = cast(Any, n) +value = type(b) +reveal_type(value) # N: Revealed type is "Union[builtins.type, types.ModuleType, Any]" +reveal_type(value.foo) # E: Item "type" of "Union[type, Module, Any]" has no attribute "foo" \ + # N: Revealed type is "Any" +[file mod.py] +def foo() -> None: ... + +[builtins fixtures/tuple.pyi]