Skip to content

Commit

Permalink
Fix --warn-return-any for NotImplemented (#4545)
Browse files Browse the repository at this point in the history
fixes #4534
  • Loading branch information
drtyrsa authored and gvanrossum committed Feb 6, 2018
1 parent 7ba2df4 commit 26b51e5
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
12 changes: 10 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
from mypy.meet import is_overlapping_types
from mypy.options import Options
from mypy.plugin import Plugin, CheckerPluginInterface
from mypy.sharedparse import BINARY_MAGIC_METHODS

from mypy import experiments

Expand Down Expand Up @@ -2179,8 +2180,11 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
if isinstance(typ, AnyType):
# (Unless you asked to be warned in that case, and the
# function is not declared to return Any)
if (self.options.warn_return_any and not self.current_node_deferred and
not is_proper_subtype(AnyType(TypeOfAny.special_form), return_type)):
if (self.options.warn_return_any
and not self.current_node_deferred
and not is_proper_subtype(AnyType(TypeOfAny.special_form), return_type)
and not (defn.name() in BINARY_MAGIC_METHODS and
is_literal_not_implemented(s.expr))):
self.msg.incorrectly_returning_any(return_type, s)
return

Expand Down Expand Up @@ -3232,6 +3236,10 @@ def remove_optional(typ: Type) -> Type:
return typ


def is_literal_not_implemented(n: Expression) -> bool:
return isinstance(n, NameExpr) and n.fullname == 'builtins.NotImplemented'


def builtin_item_type(tp: Type) -> Optional[Type]:
"""Get the item type of a builtin container.
Expand Down
47 changes: 46 additions & 1 deletion mypy/sharedparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"__delitem__",
"__divmod__",
"__div__",
"__divmod__",
"__enter__",
"__exit__",
"__eq__",
Expand Down Expand Up @@ -92,6 +91,52 @@

MAGIC_METHODS_POS_ARGS_ONLY = MAGIC_METHODS - MAGIC_METHODS_ALLOWING_KWARGS

BINARY_MAGIC_METHODS = {
"__add__",
"__and__",
"__cmp__",
"__divmod__",
"__div__",
"__eq__",
"__floordiv__",
"__ge__",
"__gt__",
"__iadd__",
"__iand__",
"__idiv__",
"__ifloordiv__",
"__ilshift__",
"__imod__",
"__imul__",
"__ior__",
"__ipow__",
"__irshift__",
"__isub__",
"__ixor__",
"__le__",
"__lshift__",
"__lt__",
"__mod__",
"__mul__",
"__or__",
"__pow__",
"__radd__",
"__rand__",
"__rdiv__",
"__rfloordiv__",
"__rlshift__",
"__rmod__",
"__rmul__",
"__ror__",
"__rpow__",
"__rrshift__",
"__rshift__",
"__rsub__",
"__rxor__",
"__sub__",
"__xor__",
}


def special_function_elide_names(name: str) -> bool:
return name in MAGIC_METHODS_POS_ARGS_ONLY
Expand Down
15 changes: 15 additions & 0 deletions test-data/unit/check-warnings.test
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ def f() -> int: return g()
[out]
main:4: warning: Returning Any from function declared to return "int"

[case testReturnAnyForNotImplementedInBinaryMagicMethods]
# flags: --warn-return-any
class A:
def __eq__(self, other: object) -> bool: return NotImplemented
[builtins fixtures/notimplemented.pyi]
[out]

[case testReturnAnyForNotImplementedInNormalMethods]
# flags: --warn-return-any
class A:
def some(self) -> bool: return NotImplemented
[builtins fixtures/notimplemented.pyi]
[out]
main:3: warning: Returning Any from function declared to return "bool"

[case testReturnAnyFromTypedFunctionWithSpecificFormatting]
# flags: --warn-return-any
from typing import Any, Tuple
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/fixtures/notimplemented.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# builtins stub used in NotImplemented related cases.
from typing import Any, cast


class object:
def __init__(self) -> None: pass

class type: pass
class function: pass
class bool: pass
class int: pass
class str: pass
NotImplemented = cast(Any, None)

0 comments on commit 26b51e5

Please sign in to comment.