From c7a3fc4ab415276468bc2be00837b0bac9cbf28a Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 22 Jul 2022 18:41:53 -0700 Subject: [PATCH 1/3] Check implicit None return is valid when using `--no-warn-no-return` Fixes #7511 --- mypy/checker.py | 33 ++++++++++++++++++---------- test-data/unit/check-flags.test | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c131e80d47f0..9aca3165f8d4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1040,7 +1040,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) self.accept(item.body) unreachable = self.binder.is_unreachable() - if self.options.warn_no_return and not unreachable: + if not unreachable and not body_is_trivial: if (defn.is_generator or is_named_instance(self.return_types[-1], 'typing.AwaitableGenerator')): return_type = self.get_generator_return_type(self.return_types[-1], @@ -1049,17 +1049,28 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) return_type = self.get_coroutine_return_type(self.return_types[-1]) else: return_type = self.return_types[-1] - return_type = get_proper_type(return_type) - if not isinstance(return_type, (NoneType, AnyType)) and not body_is_trivial: - # Control flow fell off the end of a function that was - # declared to return a non-None type and is not - # entirely pass/Ellipsis/raise NotImplementedError. - if isinstance(return_type, UninhabitedType): - # This is a NoReturn function - self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) - else: - self.fail(message_registry.MISSING_RETURN_STATEMENT, defn) + + if self.options.warn_no_return: + if not isinstance(return_type, (NoneType, AnyType)): + # Control flow fell off the end of a function that was + # declared to return a non-None type and is not + # entirely pass/Ellipsis/raise NotImplementedError. + if isinstance(return_type, UninhabitedType): + # This is a NoReturn function + self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) + else: + self.fail(message_registry.MISSING_RETURN_STATEMENT, defn) + elif not self.options.warn_no_return: + # similar to code in check_return_stmt + self.check_subtype( + subtype_label='implicitly returns', + subtype=NoneType(), + supertype_label='expected', + supertype=return_type, + context=defn, + msg=message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE, + code=codes.RETURN_VALUE) self.return_types.pop() diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 5cda01fab855..19a02bc3ad72 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -410,6 +410,44 @@ async def h() -> NoReturn: # E: Implicit return in function which does not retu [builtins fixtures/dict.pyi] [typing fixtures/typing-async.pyi] +[case testNoWarnNoReturn] +# flags: --no-warn-no-return --strict-optional +import typing + +def implicit_optional_return(arg) -> typing.Optional[str]: + if arg: + return "false" + +def unsound_implicit_return(arg) -> str: # E: Incompatible return value type (implicitly returns "None", expected "str") + if arg: + return "false" + +def implicit_return_gen(arg) -> typing.Generator[int, None, typing.Optional[str]]: + yield 1 + +def unsound_implicit_return_gen(arg) -> typing.Generator[int, None, str]: # E: Incompatible return value type (implicitly returns "None", expected "str") + yield 1 +[builtins fixtures/dict.pyi] + +[case testNoWarnNoReturnNoStrictOptional] +# flags: --no-warn-no-return --no-strict-optional +import typing + +def implicit_optional_return(arg) -> typing.Optional[str]: + if arg: + return "false" + +def unsound_implicit_return(arg) -> str: + if arg: + return "false" + +def implicit_return_gen(arg) -> typing.Generator[int, None, typing.Optional[str]]: + yield 1 + +def unsound_implicit_return_gen(arg) -> typing.Generator[int, None, str]: + yield 1 +[builtins fixtures/dict.pyi] + [case testNoReturnImportFromTyping] from typing import NoReturn From f1df60a78ff69b551794aaf0657e4f6a8321c66a Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 22 Jul 2022 19:06:57 -0700 Subject: [PATCH 2/3] fix random other test --- test-data/unit/check-inline-config.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 2eb41eade6fa..578d8eff7ff8 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -114,8 +114,8 @@ import a [file a.py] # mypy: no-warn-no-return -from typing import List -def foo() -> List: +from typing import Optional, List +def foo() -> Optional[List]: 20 [file b.py.2] From 039de86bfd288e93aa45791505f31810d3507e2e Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Fri, 22 Jul 2022 22:55:21 -0700 Subject: [PATCH 3/3] unnecessary elif --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9aca3165f8d4..90913ed3f86b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1061,7 +1061,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn) else: self.fail(message_registry.MISSING_RETURN_STATEMENT, defn) - elif not self.options.warn_no_return: + else: # similar to code in check_return_stmt self.check_subtype( subtype_label='implicitly returns',