diff --git a/mypy/checker.py b/mypy/checker.py index ea3e9f072afc..8c90ec613aab 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1486,7 +1486,7 @@ def check_func_def( ): show_error = False - if show_error: + if show_error and not self.current_node_deferred: may_be_abstract = ( body_is_trivial and defn.info is not FUNC_NO_INFO @@ -1494,9 +1494,7 @@ def check_func_def( and defn.info.metaclass_type.type.has_base("abc.ABCMeta") ) if self.options.warn_no_return: - if not self.current_node_deferred and not isinstance( - return_type, (NoneType, AnyType) - ): + 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. if isinstance(return_type, UninhabitedType): @@ -3154,6 +3152,13 @@ def visit_block(self, b: Block) -> None: self.binder.unreachable() return for s in b.body: + # If we export types, we need to continue checking to make sure all + # expressions are present in type map (even if most have type Any). + if self.current_node_deferred and not self.options.export_types: + # With current deferral logic there is no point continuing to + # type-check current function, as we will not infer more types, + # will not show errors, and each expression is inferred as Any. + return if self.binder.is_unreachable(): if not self.should_report_unreachable_issues(): break @@ -4713,6 +4718,7 @@ def check_simple_assignment( and inferred is not None and inferred.type is not None and not inferred.is_final + and not self.current_node_deferred ): new_inferred = remove_instance_last_known_values(rvalue_type) if not is_same_type(inferred.type, new_inferred): diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 97d4d59b9c03..7eb6723844b4 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3750,3 +3750,20 @@ foo(*args) # E: Argument 1 to "foo" has incompatible type "*list[object]"; expe kwargs: dict[str, object] foo(**kwargs) # E: Argument 1 to "foo" has incompatible type "**dict[str, object]"; expected "P" [builtins fixtures/dict.pyi] + +[case testNoImplicitReturnErrorOnDeferral_no_empty] +# flags: --no-warn-no-return +def deferred() -> int: + c: C + x = 1 + if int(): + x = c.x + reveal_type(x) # N: Revealed type is "builtins.int" + return x + +class C: + def __init__(self) -> None: + self.x = defer() + +def defer() -> int: + return 1 diff --git a/test-data/unit/check-redefine2.test b/test-data/unit/check-redefine2.test index 6e9441151c93..022e5fe9d209 100644 --- a/test-data/unit/check-redefine2.test +++ b/test-data/unit/check-redefine2.test @@ -762,6 +762,21 @@ def f2() -> None: reveal_type(x) # N: Revealed type is "builtins.str" reveal_type(x) # N: Revealed type is "builtins.int | builtins.str" +[case testNewRedefineNoWideningDeferred] +# flags: --allow-redefinition-new --local-partial-types +def deferred() -> None: + c: C + x = 1 + if int(): + x = c.x + reveal_type(x) # N: Revealed type is "builtins.int" + +class C: + def __init__(self) -> None: + self.x = defer() + +def defer() -> int: ... + [case testNewRedefineTryStatement] # flags: --allow-redefinition-new --local-partial-types class E(Exception): pass