Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
):
Expand Down
46 changes: 46 additions & 0 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
-- -------
Expand Down
Loading