Skip to content

Add an optional warning for returning Any from typed functions #2854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 14, 2017
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ Here are some more useful flags:
also currently ignores functions with an empty body or a body that is
just ellipsis (``...``), since these can be valid as abstract methods.

- ``--warn-return-any`` causes mypy to generate a warning when returning a value
with type ``Any`` from a function declared with a non- ``Any`` return type.

- ``--strict-boolean`` will make using non-boolean expressions in conditions
an error. This means ``if x`` and ``while x`` are disallowed when ``x`` has any
type other than ``bool``. Instead use explicit checks like ``if x > 0`` or
Expand Down
5 changes: 5 additions & 0 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ overridden by the pattern sections matching the module name.
- ``warn_no_return`` (Boolean, default False) shows errors for
missing return statements on some execution paths.

- ``warn_return_any`` (Boolean, default False) shows a warning when
returning a value with type ``Any`` from a function declared with a
non- ``Any`` return type.


Example
*******

Expand Down
4 changes: 4 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,10 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
typ = self.expr_checker.accept(s.expr, return_type)
# Returning a value of type Any is always fine.
if isinstance(typ, AnyType):
# (Unless you asked to be warned in that case, and the
# function is not declared to return Any)
if not isinstance(return_type, AnyType) and self.options.warn_return_any:
self.warn(messages.RETURN_ANY.format(return_type), s)
return

if self.is_unusable_type(return_type):
Expand Down
3 changes: 3 additions & 0 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ def add_invertible_flag(flag: str,
help="warn about casting an expression to its inferred type")
add_invertible_flag('--no-warn-no-return', dest='warn_no_return', default=True,
help="do not warn about functions that end without returning")
add_invertible_flag('--warn-return-any', default=False, strict_flag=True,
help="warn about returning values of type Any"
" from non-Any typed functions")
add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True,
help="warn about unneeded '# type: ignore' comments")
add_invertible_flag('--show-error-context', default=True,
Expand Down
1 change: 1 addition & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
MISSING_RETURN_STATEMENT = 'Missing return statement'
INVALID_IMPLICIT_RETURN = 'Implicit return in function which does not return'
INCOMPATIBLE_RETURN_VALUE_TYPE = 'Incompatible return value type'
RETURN_ANY = 'Returning Any from function with declared return type "{}"'
RETURN_VALUE_EXPECTED = 'Return value expected'
NO_RETURN_EXPECTED = 'Return statement in function which does not return'
INVALID_EXCEPTION = 'Exception must be derived from BaseException'
Expand Down
5 changes: 5 additions & 0 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Options:
"strict_optional_whitelist",
"show_none_errors",
"warn_no_return",
"warn_return_any",
"ignore_errors",
"strict_boolean",
}
Expand Down Expand Up @@ -65,6 +66,10 @@ def __init__(self) -> None:
# Warn about falling off the end of a function returning non-None
self.warn_no_return = True

# Warn about returning objects of type Any when the function is
# declared with a precise type
self.warn_return_any = False

# Warn about unused '# type: ignore' comments
self.warn_unused_ignores = False

Expand Down
35 changes: 35 additions & 0 deletions test-data/unit/check-warnings.test
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,38 @@ def f() -> int:
# This might be an @abstractmethod, for example
pass
[out]


-- Returning Any
-- -------------

[case testReturnAnyFromTypedFunction]
# flags: --warn-return-any
from typing import Any
def g() -> Any: pass
def f() -> int: return g()
[out]
main:4: warning: Returning Any from function with declared return type "builtins.int"

[case testReturnAnySilencedFromTypedFunction]
# flags: --warn-return-any
from typing import Any
def g() -> Any: pass
def f() -> int:
result = g() # type: int
return result
[out]

[case testReturnAnyFromUntypedFunction]
# flags: --warn-return-any
from typing import Any
def g() -> Any: pass
def f(): return g()
[out]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be good to have a test for returning Any from a function declared to return Any.


[case testReturnAnyFromAnyTypedFunction]
# flags: --warn-return-any
from typing import Any
def g() -> Any: pass
def f() -> Any: return g()
[out]