diff --git a/mypy/checker.py b/mypy/checker.py index 33705c98e10c..2bc77196a510 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8128,7 +8128,20 @@ def iterable_item_type(self, it: ProperType, context: Context) -> Type: return self.analyze_iterable_item_type_without_expression(it, context)[1] def function_type(self, func: FuncBase) -> FunctionLike: - return function_type(func, self.named_type("builtins.function")) + typ = function_type(func, self.named_type("builtins.function")) + if ( + isinstance(func, FuncItem) + and func.is_coroutine + and not func.is_async_generator + and func.type is None + and isinstance(typ, CallableType) + ): + any_type = AnyType(TypeOfAny.special_form) + ret_type = self.named_generic_type( + "typing.Coroutine", [any_type, any_type, typ.ret_type] + ) + return typ.copy_modified(ret_type=ret_type) + return typ def push_type_map(self, type_map: TypeMap, *, from_assignment: bool = True) -> None: if is_unreachable_map(type_map): diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py index 2b25d3e73a6f..c1c4cee748b3 100644 --- a/mypy/checker_shared.py +++ b/mypy/checker_shared.py @@ -16,6 +16,7 @@ ArgKind, Context, Expression, + FuncBase, FuncItem, LambdaExpr, MypyFile, @@ -28,6 +29,7 @@ from mypy.plugin import CheckerPluginInterface, Plugin from mypy.types import ( CallableType, + FunctionLike, Instance, LiteralValue, Overloaded, @@ -149,6 +151,10 @@ def expr_checker(self) -> ExpressionCheckerSharedApi: def named_type(self, name: str) -> Instance: raise NotImplementedError + @abstractmethod + def function_type(self, func: FuncBase) -> FunctionLike: + raise NotImplementedError + @abstractmethod def lookup_typeinfo(self, fullname: str) -> TypeInfo: raise NotImplementedError diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 44855f49afaf..172d44555b94 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -150,7 +150,6 @@ false_only, fixup_partial_type, freeze_all_type_vars, - function_type, get_all_type_vars, get_type_vars, is_literal_type_like, @@ -415,7 +414,7 @@ def analyze_static_reference( if isinstance(node, (Var, Decorator, OverloadedFuncDef)): return node.type or AnyType(TypeOfAny.special_form) elif isinstance(node, FuncDef): - return function_type(node, self.named_type("builtins.function")) + return self.chk.function_type(node) elif isinstance(node, TypeInfo): # Reference to a type object. if node.typeddict_type: diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e75a8ed7a5b0..3ba99d8e8c6b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -47,7 +47,6 @@ bind_self, erase_to_bound, freeze_all_type_vars, - function_type, get_all_type_vars, make_simplified_union, supported_self_type, @@ -360,7 +359,7 @@ def analyze_instance_member_access( if mx.is_lvalue and not mx.suppress_errors: mx.msg.cant_assign_to_method(mx.context) if not isinstance(method, OverloadedFuncDef): - signature = function_type(method, mx.named_type("builtins.function")) + signature = mx.chk.function_type(method) else: if method.type is None: # Overloads may be not ready if they are decorated. Handle this in same @@ -1325,7 +1324,7 @@ def analyze_class_attribute_access( return AnyType(TypeOfAny.from_error) else: assert isinstance(node.node, SYMBOL_FUNCBASE_TYPES) - typ = function_type(node.node, mx.named_type("builtins.function")) + typ = mx.chk.function_type(node.node) # Note: if we are accessing class method on class object, the cls argument is bound. # Annotated and/or explicit class methods go through other code paths above, for # unannotated implicit class methods we do this here. @@ -1490,7 +1489,7 @@ def analyze_decorator_or_funcbase_access( """ if isinstance(defn, Decorator): return analyze_var(name, defn.var, itype, mx) - typ = function_type(defn, mx.chk.named_type("builtins.function")) + typ = mx.chk.function_type(defn) if isinstance(defn, (FuncDef, OverloadedFuncDef)) and defn.is_trivial_self: return bind_self_fast(typ, mx.self_type) typ = check_self_arg(typ, mx.self_type, defn.is_class, mx.context, name, mx.msg) diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index e887b4c57552..cd40f2cf29a6 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -839,6 +839,34 @@ def bar() -> None: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] +[case testUntypedAsyncFunctionAndMethodReturnCoroutine] +# flags: --show-error-codes +async def foo(): + pass + +class C: + async def method(self): + pass + +def f(c: C) -> None: + a = foo() + b = c.method() + reveal_type(foo) # N: Revealed type is "def () -> typing.Coroutine[Any, Any, Any]" + reveal_type(a) # E: Value of type "Coroutine[Any, Any, Any]" must be used [unused-coroutine] \ + # N: Are you missing an await? \ + # N: Revealed type is "typing.Coroutine[Any, Any, Any]" + foo() # E: Value of type "Coroutine[Any, Any, Any]" must be used [unused-coroutine] \ + # N: Are you missing an await? + reveal_type(c.method) # N: Revealed type is "def () -> typing.Coroutine[Any, Any, Any]" + reveal_type(b) # E: Value of type "Coroutine[Any, Any, Any]" must be used [unused-coroutine] \ + # N: Are you missing an await? \ + # N: Revealed type is "typing.Coroutine[Any, Any, Any]" + c.method() # E: Value of type "Coroutine[Any, Any, Any]" must be used [unused-coroutine] \ + # N: Are you missing an await? + +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] + [case testAsyncForOutsideCoroutine] async def g(): yield 0 diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index dd4687181ca4..acdee2a10430 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -59,7 +59,7 @@ async def f(): # E: Function is missing a return type annotation \ # N: Use "-> None" if function does not return a value pass [builtins fixtures/async_await.pyi] -[typing fixtures/typing-medium.pyi] +[typing fixtures/typing-async.pyi] [case testAsyncUnannotatedArgument] # flags: --disallow-untyped-defs