diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 17bedf79053a..483e5eb4bc42 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -344,9 +344,19 @@ def fail(self, msg: str, line: int, column: int, - blocker: bool = True) -> None: + blocker: bool = True, + code: codes.ErrorCode = codes.SYNTAX) -> None: if blocker or not self.options.ignore_errors: - self.errors.report(line, column, msg, blocker=blocker, code=codes.SYNTAX) + self.errors.report(line, column, msg, blocker=blocker, code=code) + + def fail_merge_overload(self, node: IfStmt) -> None: + self.fail( + "Condition can't be inferred, unable to merge overloads", + line=node.line, + column=node.column, + blocker=False, + code=codes.MISC, + ) def visit(self, node: Optional[AST]) -> Any: if node is None: @@ -479,10 +489,12 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: last_if_stmt: Optional[IfStmt] = None last_if_overload: Optional[Union[Decorator, FuncDef, OverloadedFuncDef]] = None last_if_stmt_overload_name: Optional[str] = None + last_if_unknown_truth_value: Optional[IfStmt] = None skipped_if_stmts: List[IfStmt] = [] for stmt in stmts: if_overload_name: Optional[str] = None if_block_with_overload: Optional[Block] = None + if_unknown_truth_value: Optional[IfStmt] = None if ( isinstance(stmt, IfStmt) and len(stmt.body[0].body) == 1 @@ -495,7 +507,8 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: # Check IfStmt block to determine if function overloads can be merged if_overload_name = self._check_ifstmt_for_overloads(stmt) if if_overload_name is not None: - if_block_with_overload = self._get_executable_if_block_with_overloads(stmt) + if_block_with_overload, if_unknown_truth_value = \ + self._get_executable_if_block_with_overloads(stmt) if (current_overload_name is not None and isinstance(stmt, (Decorator, FuncDef)) @@ -510,6 +523,9 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: else: current_overload.append(last_if_overload) last_if_stmt, last_if_overload = None, None + if last_if_unknown_truth_value: + self.fail_merge_overload(last_if_unknown_truth_value) + last_if_unknown_truth_value = None current_overload.append(stmt) elif ( current_overload_name is not None @@ -522,6 +538,8 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: # or function definitions. skipped_if_stmts.append(stmt) if if_block_with_overload is None: + if if_unknown_truth_value is not None: + self.fail_merge_overload(if_unknown_truth_value) continue if last_if_overload is not None: # Last stmt was an IfStmt with same overload name @@ -542,6 +560,7 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: ret.append(last_if_stmt) last_if_stmt_overload_name = current_overload_name last_if_stmt, last_if_overload = None, None + last_if_unknown_truth_value = None if current_overload and current_overload_name == last_if_stmt_overload_name: # Remove last stmt (IfStmt) from ret if the overload names matched @@ -580,6 +599,7 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: Union[Decorator, FuncDef, OverloadedFuncDef], if_block_with_overload.body[0] ) + last_if_unknown_truth_value = if_unknown_truth_value else: current_overload = [] current_overload_name = None @@ -630,29 +650,40 @@ def _check_ifstmt_for_overloads(self, stmt: IfStmt) -> Optional[str]: return None - def _get_executable_if_block_with_overloads(self, stmt: IfStmt) -> Optional[Block]: + def _get_executable_if_block_with_overloads( + self, stmt: IfStmt + ) -> Tuple[Optional[Block], Optional[IfStmt]]: """Return block from IfStmt that will get executed. - Only returns block if sure that alternative blocks are unreachable. + Return + 0 -> A block if sure that alternative blocks are unreachable. + 1 -> An IfStmt if the reachability of it can't be inferred, + i.e. the truth value is unknown. """ infer_reachability_of_if_statement(stmt, self.options) + if ( + stmt.else_body is None + and stmt.body[0].is_unreachable is True + ): + # always False condition with no else + return None, None if ( stmt.else_body is None or stmt.body[0].is_unreachable is False and stmt.else_body.is_unreachable is False ): # The truth value is unknown, thus not conclusive - return None + return None, stmt if stmt.else_body.is_unreachable is True: # else_body will be set unreachable if condition is always True - return stmt.body[0] + return stmt.body[0], None if stmt.body[0].is_unreachable is True: # body will be set unreachable if condition is always False # else_body can contain an IfStmt itself (for elif) -> do a recursive check if isinstance(stmt.else_body.body[0], IfStmt): return self._get_executable_if_block_with_overloads(stmt.else_body.body[0]) - return stmt.else_body - return None + return stmt.else_body, None + return None, stmt def _strip_contents_from_if_stmt(self, stmt: IfStmt) -> None: """Remove contents from IfStmt. diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 511c51e84342..331a80f7d2ac 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -5463,7 +5463,8 @@ reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" def f3(g: A) -> A: ... @overload def f3(g: B) -> B: ... -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f3(g: C) -> C: ... def f3(g): ... @@ -5777,7 +5778,8 @@ reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... elif True: @@ -5795,7 +5797,8 @@ reveal_type(f3(B())) # E: No overload variant of "f3" matches argument type "B" @overload # E: Single overload definition, multiple required def f4(x: A) -> A: ... -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f4(x: B) -> B: ... else: @@ -5816,6 +5819,7 @@ class A: ... class B: ... class C: ... class D: ... +class E: ... # ----- # Match the first always-true block @@ -5844,7 +5848,8 @@ def f2(x: A) -> A: ... if False: @overload def f2(x: B) -> B: ... -elif maybe_true: # E: Name "maybe_true" is not defined +elif maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f2(x: C) -> C: ... else: @@ -5859,7 +5864,8 @@ reveal_type(f2(C())) # E: No overload variant of "f2" matches argument type "C" @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... elif False: @@ -5875,6 +5881,30 @@ reveal_type(f3(B())) # E: No overload variant of "f3" matches argument type "B" # N: def f3(x: A) -> A \ # N: Revealed type is "Any" +def g(bool_var: bool) -> None: + @overload + def f4(x: A) -> A: ... + if bool_var: # E: Condition cannot be inferred, unable to merge overloads + @overload + def f4(x: B) -> B: ... + elif maybe_true: # E: Name "maybe_true" is not defined + # No 'Condition cannot be inferred' error here since it's already + # emitted on the first condition, 'bool_var', above. + @overload + def f4(x: C) -> C: ... + else: + @overload + def f4(x: D) -> D: ... + @overload + def f4(x: E) -> E: ... + def f4(x): ... + reveal_type(f4(E())) # N: Revealed type is "__main__.E" + reveal_type(f4(B())) # E: No overload variant of "f4" matches argument type "B" \ + # N: Possible overload variants: \ + # N: def f4(x: A) -> A \ + # N: def f4(x: E) -> E \ + # N: Revealed type is "Any" + [case testOverloadIfSkipUnknownExecution] # flags: --always-true True from typing import overload @@ -5891,13 +5921,15 @@ class D: ... @overload # E: Single overload definition, multiple required def f1(x: A) -> A: ... -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f1(x: B) -> B: ... def f1(x): ... reveal_type(f1(A())) # N: Revealed type is "__main__.A" -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f2(x: A) -> A: ... @overload @@ -5914,14 +5946,16 @@ reveal_type(f2(A())) # E: No overload variant of "f2" matches argument type "A" if True: @overload # E: Single overload definition, multiple required def f3(x: A) -> A: ... - if maybe_true: # E: Name "maybe_true" is not defined + if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f3(x: B) -> B: ... def f3(x): ... reveal_type(f3(A())) # N: Revealed type is "__main__.A" if True: - if maybe_true: # E: Name "maybe_true" is not defined + if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f4(x: A) -> A: ... @overload @@ -6178,10 +6212,12 @@ reveal_type(f8(C())) # E: No overload variant of "f8" matches argument type "C" # N: def f8(x: B) -> B \ # N: Revealed type is "Any" -if maybe_true: # E: Name "maybe_true" is not defined +if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f9(x: A) -> A: ... -if another_maybe_true: # E: Name "another_maybe_true" is not defined +if another_maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "another_maybe_true" is not defined @overload def f9(x: B) -> B: ... @overload @@ -6197,10 +6233,12 @@ reveal_type(f9(A())) # E: No overload variant of "f9" matches argument type "A" reveal_type(f9(C())) # N: Revealed type is "__main__.C" if True: - if maybe_true: # E: Name "maybe_true" is not defined + if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined @overload def f10(x: A) -> A: ... - if another_maybe_true: # E: Name "another_maybe_true" is not defined + if another_maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "another_maybe_true" is not defined @overload def f10(x: B) -> B: ... @overload