Skip to content

Commit

Permalink
Warn for missing returns with explicit Any return types
Browse files Browse the repository at this point in the history
As discussed in python#7511, mypy's
`--warn-no-return` isn't really a question of type safety. It does
however enforce a rule that is "dear to Guido's heart", and I think we
should enforce it for functions that explicitly return Any as well.

Fixes python#16095
  • Loading branch information
hauntsaninja committed Sep 13, 2023
1 parent 8b73cc2 commit bb5d439
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 2 deletions.
6 changes: 5 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,11 @@ def check_func_def(
if self.options.warn_no_return:
if (
not self.current_node_deferred
and not isinstance(return_type, (NoneType, AnyType))
and not isinstance(return_type, NoneType)
and (
not isinstance(return_type, AnyType)
or return_type.type_of_any == TypeOfAny.explicit
)
and show_error
):
# Control flow fell off the end of a function that was
Expand Down
3 changes: 3 additions & 0 deletions test-data/unit/check-overloading.test
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def f(x: Any) -> Any:
foo = 1
if int():
foo = "bar" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
return foo

@overload
def g(x: 'A') -> 'B': ...
Expand Down Expand Up @@ -4906,13 +4907,15 @@ def f() -> None:
def g(x: T) -> Dict[int, T]: ...
def g(*args, **kwargs) -> Any:
reveal_type(h(C())) # N: Revealed type is "builtins.dict[builtins.str, __main__.C]"
return args

@overload
def h() -> None: ...
@overload
def h(x: T) -> Dict[str, T]: ...
def h(*args, **kwargs) -> Any:
reveal_type(g(C())) # N: Revealed type is "builtins.dict[builtins.int, __main__.C]"
return args

[builtins fixtures/dict.pyi]
[out]
Expand Down
18 changes: 18 additions & 0 deletions test-data/unit/check-warnings.test
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@ def f() -> int:
pass
[out]

[case testNoReturnExplicitAny]
# flags: --warn-no-return
from typing import Any
from unknown import Mystery # type: ignore[import]

def maybe() -> bool: ...

def implicit(x: Any):
if maybe():
return x

def explicit(x: Any) -> Any: # E: Missing return statement
if maybe():
return x

def mystery(x: Any) -> Mystery:
if maybe():
return x

-- Returning Any
-- -------------
Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/deps.test
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ def f(x: str) -> submod.B: pass
def f(x) -> Any:
y: submod.C
y.x
return y
[file submod.py]
class A: pass
class B: pass
Expand Down Expand Up @@ -959,6 +960,7 @@ def f(x: int) -> None: pass
def f(x: str) -> str: pass
def f(x: Any) -> Any:
mod.g()
return mod
[file mod.py]
def g() -> int:
pass
Expand All @@ -976,6 +978,7 @@ def outer() -> None:
def f(x: str) -> mod.B: pass
def f(x: Any) -> Any:
mod.g()
return mod
[file mod.py]
def g() -> int:
pass
Expand All @@ -997,6 +1000,7 @@ def outer() -> None:
def f(x: str) -> str: pass
def f(x: Any) -> Any:
mod.g(str())
return mod
[file mod.py]
from typing import overload
@overload
Expand All @@ -1018,7 +1022,7 @@ class Outer:
@overload
def f(self, x: str, cb=mod.g) -> str: pass
def f(self, *args: Any, **kwargs: Any) -> Any:
pass
return args
[file mod.py]
from typing import overload
@overload
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/fine-grained.test
Original file line number Diff line number Diff line change
Expand Up @@ -6995,6 +6995,7 @@ def outer() -> None:
def f(x: str) -> str: pass
def f(x: Any) -> Any:
y: int = mod.f()
return y
[file mod.py]
def f() -> int:
pass
Expand Down Expand Up @@ -7257,6 +7258,7 @@ def f(x: str) -> submod.B: pass
def f(x) -> Any:
y: submod.C
y.x = int()
return y
[file submod.py]
import other
class A: pass
Expand Down

0 comments on commit bb5d439

Please sign in to comment.