Errors when merging consecutive leading-indented (?) overload statements
Bug Report, To Reproduce, & Actual Behaviour
In the following, sys.version_info >= ... can be any other compile-time constant (e.g. those set by always_true / always_false, typing.TYPE_CHECKING, etc.).
It appears that consecutive overload merge candidate blocks cause this issue; adding an additional statement between the blocks (here, # UNCOMMENT_ME_TO_FIX = ...) allow the overloads to resolve properly.
import sys
import typing as tp
if sys.version_info >= (3, 10):
@tp.overload
def a(arg: str, /) -> str: ...
@tp.overload
def a(arg: int, /) -> int: ...
def a(arg: str | int, /) -> str | int:
return arg
# UNCOMMENT_ME_TO_FIX = ...
if sys.version_info >= (3, 9):
@tp.overload # main.py:16: error: An overloaded function outside a stub file must have an implementation [no-overload-impl]
def b(arg: str, /) -> str: ...
@tp.overload
def b(arg: int, /) -> int: ...
def b(arg: str | int, /) -> str | int: # main.py:21: error: Name "b" already defined on line 16 [no-redef]
return arg
Expected Behavior
No issues
Your Environment
- Mypy version used: 1.10.1
- Mypy command-line flags: None
- Mypy configuration options from
mypy.ini(and other config files): None - Python version used: 3.12
The problem can be localised in this loop:
https://github.com/python/mypy/blob/3c9f69487d3f01b7c24cf4dbda2460c99094d1f3/mypy/fastparse.py#L606
More specifically, if the current statement terminates a previous series of analysed overloads leading to an OverloadedFuncDef to be saved,
https://github.com/python/mypy/blob/3c9f69487d3f01b7c24cf4dbda2460c99094d1f3/mypy/fastparse.py#L688-L691
the current statement is not fully re-analysed for the presence of potential overloads. That is, the following doesn't repeat the same analysis at the beginning of the loop for the current statement.
https://github.com/python/mypy/blob/3c9f69487d3f01b7c24cf4dbda2460c99094d1f3/mypy/fastparse.py#L698-L719
A quick hack to force full re-analysis of the current statement solves this particular issue:
else:
current_overload = []
current_overload_name = None
+ if isinstance(stmt, IfStmt):
+ if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name)
+ if if_overload_name is not None:
+ ret.extend(self.fix_function_overloads(stmts[stmts.index(stmt):]))
+ break
ret.append(stmt)