mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Errors when merging consecutive leading-indented (?) overload statements

Open bzoracler opened this issue 1 year ago • 1 comments

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.

mypy Playground

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

bzoracler avatar Jul 13 '24 19:07 bzoracler

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)

bzoracler avatar Jul 13 '24 21:07 bzoracler