Skip to content

Commit 95fd5fb

Browse files
lincolnqddfisher
authored andcommitted
Add an optional warning for returning Any from typed functions (#2854)
This adds an optional warning which lets you know when you're returning Any from a function which is documented to return a non-Any type.
1 parent 5748b2b commit 95fd5fb

File tree

7 files changed

+56
-0
lines changed

7 files changed

+56
-0
lines changed

docs/source/command_line.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ Here are some more useful flags:
366366
also currently ignores functions with an empty body or a body that is
367367
just ellipsis (``...``), since these can be valid as abstract methods.
368368

369+
- ``--warn-return-any`` causes mypy to generate a warning when returning a value
370+
with type ``Any`` from a function declared with a non- ``Any`` return type.
371+
369372
- ``--strict-boolean`` will make using non-boolean expressions in conditions
370373
an error. This means ``if x`` and ``while x`` are disallowed when ``x`` has any
371374
type other than ``bool``. Instead use explicit checks like ``if x > 0`` or

docs/source/config_file.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ overridden by the pattern sections matching the module name.
168168
- ``warn_no_return`` (Boolean, default False) shows errors for
169169
missing return statements on some execution paths.
170170

171+
- ``warn_return_any`` (Boolean, default False) shows a warning when
172+
returning a value with type ``Any`` from a function declared with a
173+
non- ``Any`` return type.
174+
175+
171176
Example
172177
*******
173178

mypy/checker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,10 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
16991699
typ = self.expr_checker.accept(s.expr, return_type)
17001700
# Returning a value of type Any is always fine.
17011701
if isinstance(typ, AnyType):
1702+
# (Unless you asked to be warned in that case, and the
1703+
# function is not declared to return Any)
1704+
if not isinstance(return_type, AnyType) and self.options.warn_return_any:
1705+
self.warn(messages.RETURN_ANY.format(return_type), s)
17021706
return
17031707

17041708
if self.is_unusable_type(return_type):

mypy/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ def add_invertible_flag(flag: str,
221221
help="warn about casting an expression to its inferred type")
222222
add_invertible_flag('--no-warn-no-return', dest='warn_no_return', default=True,
223223
help="do not warn about functions that end without returning")
224+
add_invertible_flag('--warn-return-any', default=False, strict_flag=True,
225+
help="warn about returning values of type Any"
226+
" from non-Any typed functions")
224227
add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True,
225228
help="warn about unneeded '# type: ignore' comments")
226229
add_invertible_flag('--show-error-context', default=True,

mypy/messages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
MISSING_RETURN_STATEMENT = 'Missing return statement'
2828
INVALID_IMPLICIT_RETURN = 'Implicit return in function which does not return'
2929
INCOMPATIBLE_RETURN_VALUE_TYPE = 'Incompatible return value type'
30+
RETURN_ANY = 'Returning Any from function with declared return type "{}"'
3031
RETURN_VALUE_EXPECTED = 'Return value expected'
3132
NO_RETURN_EXPECTED = 'Return statement in function which does not return'
3233
INVALID_EXCEPTION = 'Exception must be derived from BaseException'

mypy/options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Options:
2626
"strict_optional_whitelist",
2727
"show_none_errors",
2828
"warn_no_return",
29+
"warn_return_any",
2930
"ignore_errors",
3031
"strict_boolean",
3132
}
@@ -65,6 +66,10 @@ def __init__(self) -> None:
6566
# Warn about falling off the end of a function returning non-None
6667
self.warn_no_return = True
6768

69+
# Warn about returning objects of type Any when the function is
70+
# declared with a precise type
71+
self.warn_return_any = False
72+
6873
# Warn about unused '# type: ignore' comments
6974
self.warn_unused_ignores = False
7075

test-data/unit/check-warnings.test

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,38 @@ def f() -> int:
130130
# This might be an @abstractmethod, for example
131131
pass
132132
[out]
133+
134+
135+
-- Returning Any
136+
-- -------------
137+
138+
[case testReturnAnyFromTypedFunction]
139+
# flags: --warn-return-any
140+
from typing import Any
141+
def g() -> Any: pass
142+
def f() -> int: return g()
143+
[out]
144+
main:4: warning: Returning Any from function with declared return type "builtins.int"
145+
146+
[case testReturnAnySilencedFromTypedFunction]
147+
# flags: --warn-return-any
148+
from typing import Any
149+
def g() -> Any: pass
150+
def f() -> int:
151+
result = g() # type: int
152+
return result
153+
[out]
154+
155+
[case testReturnAnyFromUntypedFunction]
156+
# flags: --warn-return-any
157+
from typing import Any
158+
def g() -> Any: pass
159+
def f(): return g()
160+
[out]
161+
162+
[case testReturnAnyFromAnyTypedFunction]
163+
# flags: --warn-return-any
164+
from typing import Any
165+
def g() -> Any: pass
166+
def f() -> Any: return g()
167+
[out]

0 commit comments

Comments
 (0)