Skip to content

Commit c02ec4a

Browse files
authored
Check implicit None return is valid when using --no-warn-no-return (#13219)
Fixes #7511
1 parent 2e03c16 commit c02ec4a

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

mypy/checker.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
12171217
self.accept(item.body)
12181218
unreachable = self.binder.is_unreachable()
12191219

1220-
if self.options.warn_no_return and not unreachable:
1220+
if not unreachable and not body_is_trivial:
12211221
if defn.is_generator or is_named_instance(
12221222
self.return_types[-1], "typing.AwaitableGenerator"
12231223
):
@@ -1228,17 +1228,29 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
12281228
return_type = self.get_coroutine_return_type(self.return_types[-1])
12291229
else:
12301230
return_type = self.return_types[-1]
1231-
12321231
return_type = get_proper_type(return_type)
1233-
if not isinstance(return_type, (NoneType, AnyType)) and not body_is_trivial:
1234-
# Control flow fell off the end of a function that was
1235-
# declared to return a non-None type and is not
1236-
# entirely pass/Ellipsis/raise NotImplementedError.
1237-
if isinstance(return_type, UninhabitedType):
1238-
# This is a NoReturn function
1239-
self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn)
1240-
else:
1241-
self.fail(message_registry.MISSING_RETURN_STATEMENT, defn)
1232+
1233+
if self.options.warn_no_return:
1234+
if not isinstance(return_type, (NoneType, AnyType)):
1235+
# Control flow fell off the end of a function that was
1236+
# declared to return a non-None type and is not
1237+
# entirely pass/Ellipsis/raise NotImplementedError.
1238+
if isinstance(return_type, UninhabitedType):
1239+
# This is a NoReturn function
1240+
self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn)
1241+
else:
1242+
self.fail(message_registry.MISSING_RETURN_STATEMENT, defn)
1243+
else:
1244+
# similar to code in check_return_stmt
1245+
self.check_subtype(
1246+
subtype_label="implicitly returns",
1247+
subtype=NoneType(),
1248+
supertype_label="expected",
1249+
supertype=return_type,
1250+
context=defn,
1251+
msg=message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE,
1252+
code=codes.RETURN_VALUE,
1253+
)
12421254

12431255
self.return_types.pop()
12441256

test-data/unit/check-flags.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,44 @@ async def h() -> NoReturn: # E: Implicit return in function which does not retu
410410
[builtins fixtures/dict.pyi]
411411
[typing fixtures/typing-async.pyi]
412412

413+
[case testNoWarnNoReturn]
414+
# flags: --no-warn-no-return --strict-optional
415+
import typing
416+
417+
def implicit_optional_return(arg) -> typing.Optional[str]:
418+
if arg:
419+
return "false"
420+
421+
def unsound_implicit_return(arg) -> str: # E: Incompatible return value type (implicitly returns "None", expected "str")
422+
if arg:
423+
return "false"
424+
425+
def implicit_return_gen(arg) -> typing.Generator[int, None, typing.Optional[str]]:
426+
yield 1
427+
428+
def unsound_implicit_return_gen(arg) -> typing.Generator[int, None, str]: # E: Incompatible return value type (implicitly returns "None", expected "str")
429+
yield 1
430+
[builtins fixtures/dict.pyi]
431+
432+
[case testNoWarnNoReturnNoStrictOptional]
433+
# flags: --no-warn-no-return --no-strict-optional
434+
import typing
435+
436+
def implicit_optional_return(arg) -> typing.Optional[str]:
437+
if arg:
438+
return "false"
439+
440+
def unsound_implicit_return(arg) -> str:
441+
if arg:
442+
return "false"
443+
444+
def implicit_return_gen(arg) -> typing.Generator[int, None, typing.Optional[str]]:
445+
yield 1
446+
447+
def unsound_implicit_return_gen(arg) -> typing.Generator[int, None, str]:
448+
yield 1
449+
[builtins fixtures/dict.pyi]
450+
413451
[case testNoReturnImportFromTyping]
414452
from typing import NoReturn
415453

test-data/unit/check-inline-config.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ import a
114114
[file a.py]
115115
# mypy: no-warn-no-return
116116

117-
from typing import List
118-
def foo() -> List:
117+
from typing import Optional, List
118+
def foo() -> Optional[List]:
119119
20
120120

121121
[file b.py.2]

0 commit comments

Comments
 (0)