diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 08d7345452fbf..7a13c68b6fd24 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -716,6 +716,10 @@ def always_returns_none(self, node: Expression) -> bool: def defn_returns_none(self, defn: SymbolNode | None) -> bool: """Check if `defn` can _only_ return None.""" + allow_inferred = False + if isinstance(defn, Decorator): + allow_inferred = True + defn = defn.var if isinstance(defn, FuncDef): return isinstance(defn.type, CallableType) and isinstance( get_proper_type(defn.type.ret_type), NoneType @@ -725,7 +729,7 @@ def defn_returns_none(self, defn: SymbolNode | None) -> bool: if isinstance(defn, Var): typ = get_proper_type(defn.type) if ( - not defn.is_inferred + (allow_inferred or not defn.is_inferred) and isinstance(typ, CallableType) and isinstance(get_proper_type(typ.ret_type), NoneType) ): diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index d5ddc910bcd63..8576c13fdf910 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1144,6 +1144,52 @@ f() and b # E: "f" does not return a value (it only ever returns None) b or f() # E: "f" does not return a value (it only ever returns None) [builtins fixtures/bool.pyi] +[case testNoneReturnDecorated] +from typing import Any, Callable, TypeVar + +F = TypeVar('F', bound=Callable[..., Any]) + +def deco(f: F) -> F: + pass + +def deco_return_none(f: object) -> Callable[..., None]: + pass + +def deco_return_not_none(f: object) -> Callable[..., int]: + pass + +@deco +@deco +def f1() -> None: + pass + +@deco +@deco_return_none +def f2() -> int: + pass + +@deco +@deco_return_not_none +def f3() -> None: + pass + +class A: + @staticmethod + def s() -> None: + pass + +if int(): + x = f1() # E: "f1" does not return a value (it only ever returns None) +if int(): + x = f2() # E: "f2" does not return a value (it only ever returns None) +if int(): + x = f3() +if int(): + x = A.s() # E: "s" of "A" does not return a value (it only ever returns None) +if int(): + x = A().s() # E: "s" of "A" does not return a value (it only ever returns None) +[builtins fixtures/staticmethod.pyi] + -- Slicing -- -------