Repository: python/mypy

Issue:
show more than two overloads in error message
* feature request
* 

```
from typing import overload
@overload
def foo(x: int) -> None: ...
@overload
def foo(x: str) -> None: ...
@overload
def foo(x: bytes) -> None: ...
@overload
def foo(x: bytearray) -> None: ...
def foo(x): return None
def main() -> None: foo(None)
```

* Even with verbose, the error message shows only two overloads

```
$ mypy foo.py 
foo.py:9: error: Overloaded function signature 4 will never be matched: signature 3's parameter type(s) are the same or broader
foo.py:11: error: No overload variant of "foo" matches argument type "None"
foo.py:11: note: Possible overload variants:
foo.py:11: note:     def foo(x: int) -> None
foo.py:11: note:     def foo(x: str) -> None
foo.py:11: note:     <2 more similar overloads not shown, out of 4 total overloads>
Found 2 errors in 1 file (checked 1 source file)
$ mypy -vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv foo.py 2>&1 | grep 'def foo'
foo.py:11: note:     def foo(x: int) -> None
foo.py:11: note:     def foo(x: str) -> None
```

* I would expect increased verbosity to increase the number of overloads shown, in case someone wants to see them all
* 

```
$ mypy --version
mypy 0.782
$ head -1 `which mypy`
#!/home/akuli/.local/bin/python3.8
```

haven't tried mypy from master yet

* verbosity flags used as shown above



Discussion:
I guess this should be very easy to implement: grep for error message and figure out how to access the verbosity level from that part of mypy's code.
happens on master

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1632,7 +1632,7 @@ def check_overload_call(self,
                 else:
                     code = codes.OPERATOR
                 arg_messages.no_variant_matches_arguments(
-                    plausible_targets, callee, arg_types, context, code=code)
+                    callee, arg_types, context, code=code)
 
         result = self.check_call(target, args, arg_kinds, context, arg_names,
                                  arg_messages=arg_messages,
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -739,7 +739,6 @@ def deleted_as_lvalue(self, typ: DeletedType, context: Context) -> None:
         self.fail('Assignment to variable{} outside except: block'.format(s), context)
 
     def no_variant_matches_arguments(self,
-                                     plausible_targets: List[CallableType],
                                      overload: Overloaded,
                                      arg_types: List[Type],
                                      context: Context,
@@ -763,8 +762,11 @@ def no_variant_matches_arguments(self,
             self.fail('No overload variant{} matches argument types {}'
                       .format(name_str, arg_types_str), context, code=code)
 
-        self.pretty_overload_matches(plausible_targets, overload, context, offset=2, max_items=2,
-                                     code=code)
+        self.note(
+            'Possible overload variant{}:'.format(plural_s(len(overload.items))),
+            context, code=code)
+        for item in overload.items:
+            self.note(pretty_callable(item), context, offset=4, code=code)
 
     def wrong_number_values_to_unpack(self, provided: int, expected: int,
                                       context: Context) -> None:
@@ -811,7 +813,6 @@ def signature_incompatible_with_supertype(
 
         INCLUDE_DECORATOR = True  # Include @classmethod and @staticmethod decorators, if any
         ALLOW_DUPS = True  # Allow duplicate notes, needed when signatures are duplicates
-        MAX_ITEMS = 3  # Display a max of three items for Overloaded types
         ALIGN_OFFSET = 1  # One space, to account for the difference between error and note
         OFFSET = 4  # Four spaces, so that notes will look like this:
         # error: Signature of "f" incompatible with supertype "A"
@@ -824,14 +825,12 @@ def signature_incompatible_with_supertype(
             self.note('Superclass:', context, offset=ALIGN_OFFSET + OFFSET, code=code)
             self.pretty_callable_or_overload(original, context, offset=ALIGN_OFFSET + 2 * OFFSET,
                                             add_class_or_static_decorator=INCLUDE_DECORATOR,
-                                            overload_max_items=MAX_ITEMS, allow_dups=ALLOW_DUPS,
-                                            code=code)
+                                            allow_dups=ALLOW_DUPS, code=code)
 
             self.note('Subclass:', context, offset=ALIGN_OFFSET + OFFSET, code=code)
             self.pretty_callable_or_overload(override, context, offset=ALIGN_OFFSET + 2 * OFFSET,
                                             add_class_or_static_decorator=INCLUDE_DECORATOR,
-                                            overload_max_items=MAX_ITEMS, allow_dups=ALLOW_DUPS,
-                                            code=code)
+                                            allow_dups=ALLOW_DUPS, code=code)
 
     def pretty_callable_or_overload(self,
                                     tp: Union[CallableType, Overloaded],
@@ -839,7 +838,6 @@ def pretty_callable_or_overload(self,
                                     *,
                                     offset: int = 0,
                                     add_class_or_static_decorator: bool = False,
-                                    overload_max_items: int = 1,
                                     allow_dups: bool = False,
                                     code: Optional[ErrorCode] = None) -> None:
         if isinstance(tp, CallableType):
@@ -850,7 +848,7 @@ def pretty_callable_or_overload(self,
             self.note(pretty_callable(tp), context,
                       offset=offset, allow_dups=allow_dups, code=code)
         elif isinstance(tp, Overloaded):
-            self.pretty_overload(tp, context, offset, overload_max_items,
+            self.pretty_overload(tp, context, offset,
                                  add_class_or_static_decorator=add_class_or_static_decorator,
                                  allow_dups=allow_dups, code=code)
 
@@ -1479,13 +1477,13 @@ def report_protocol_problems(self,
                         self.note(pretty_callable(exp), context, offset=2 * OFFSET, code=code)
                     else:
                         assert isinstance(exp, Overloaded)
-                        self.pretty_overload(exp, context, 2 * OFFSET, MAX_ITEMS, code=code)
+                        self.pretty_overload(exp, context, 2 * OFFSET, code=code)
                     self.note('Got:', context, offset=OFFSET, code=code)
                     if isinstance(got, CallableType):
                         self.note(pretty_callable(got), context, offset=2 * OFFSET, code=code)
                     else:
                         assert isinstance(got, Overloaded)
-                        self.pretty_overload(got, context, 2 * OFFSET, MAX_ITEMS, code=code)
+                        self.pretty_overload(got, context, 2 * OFFSET, code=code)
             self.print_more(conflict_types, context, OFFSET, MAX_ITEMS, code=code)
 
         # Report flag conflicts (i.e. settable vs read-only etc.)
@@ -1517,12 +1515,11 @@ def pretty_overload(self,
                         tp: Overloaded,
                         context: Context,
                         offset: int,
-                        max_items: int,
                         *,
                         add_class_or_static_decorator: bool = False,
                         allow_dups: bool = False,
                         code: Optional[ErrorCode] = None) -> None:
-        for item in tp.items[:max_items]:
+        for item in tp.items:
             self.note('@overload', context, offset=offset, allow_dups=allow_dups, code=code)
 
             if add_class_or_static_decorator:
@@ -1532,46 +1529,6 @@ def pretty_overload(self,
 
             self.note(pretty_callable(item), context,
                       offset=offset, allow_dups=allow_dups, code=code)
-        left = len(tp.items) - max_items
-        if left > 0:
-            msg = '<{} more overload{} not shown>'.format(left, plural_s(left))
-            self.note(msg, context, offset=offset, allow_dups=allow_dups, code=code)
-
-    def pretty_overload_matches(self,
-                                targets: List[CallableType],
-                                func: Overloaded,
-                                context: Context,
-                                offset: int,
-                                max_items: int,
-                                code: ErrorCode) -> None:
-        if not targets:
-            targets = func.items
-
-        shown = min(max_items, len(targets))
-        max_matching = len(targets)
-        max_available = len(func.items)
-
-        # If there are 3 matches but max_items == 2, we might as well show
-        # all three items instead of having the 3rd item be an error message.
-        if shown + 1 == max_matching:
-            shown = max_matching
-
-        self.note('Possible overload variant{}:'.format(plural_s(shown)), context, code=code)
-        for item in targets[:shown]:
-            self.note(pretty_callable(item), context, offset=2 * offset, code=code)
-
-        assert shown <= max_matching <= max_available
-        if shown < max_matching <= max_available:
-            left = max_matching - shown
-            msg = '<{} more similar overload{} not shown, out of {} total overloads>'.format(
-                left, plural_s(left), max_available)
-            self.note(msg, context, offset=2 * offset, code=code)
-        elif shown == max_matching < max_available:
-            left = max_available - shown
-            msg = '<{} more non-matching overload{} not shown>'.format(left, plural_s(left))
-            self.note(msg, context, offset=2 * offset, code=code)
-        else:
-            assert shown == max_matching == max_available
 
     def print_more(self,
                    conflicts: Sequence[Any],


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -2897,9 +2897,9 @@ c = C(1)
 c.a # E: "C" has no attribute "a"
 C('', '')
 C('') # E: No overload variant of "C" matches argument type "str" \
-      # N: Possible overload variant: \
+      # N: Possible overload variants: \
       # N:     def __new__(cls, foo: int) -> C \
-      # N:     <1 more non-matching overload not shown>
+      # N:     def __new__(cls, x: str, y: str) -> C
 [builtins fixtures/__new__.pyi]
 
 
@@ -3325,9 +3325,9 @@ u = new(User)
 [builtins fixtures/classmethod.pyi]
 [out]
 tmp/foo.pyi:17: error: No overload variant of "User" matches argument type "str"
-tmp/foo.pyi:17: note: Possible overload variant:
+tmp/foo.pyi:17: note: Possible overload variants:
+tmp/foo.pyi:17: note:     def __init__(self) -> U
 tmp/foo.pyi:17: note:     def __init__(self, arg: int) -> U
-tmp/foo.pyi:17: note:     <1 more non-matching overload not shown>
 tmp/foo.pyi:18: error: Too many arguments for "foo" of "User"
 
 [case testTypeUsingTypeCInUpperBound]
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -224,9 +224,10 @@ from dataclasses import dataclass, field
 class Person:
     name: str
     age: int = field(init=None)  # E: No overload variant of "field" matches argument type "None" \
-                                 # N: Possible overload variant: \
-                                 # N:     def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any \
-                                 # N:     <2 more non-matching overloads not shown>
+                                 # N: Possible overload variants: \
+                                 # N:     def [_T] field(*, default: _T, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T \
+                                 # N:     def [_T] field(*, default_factory: Callable[[], _T], init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> _T \
+                                 # N:     def field(*, init: bool = ..., repr: bool = ..., hash: Optional[bool] = ..., compare: bool = ..., metadata: Optional[Mapping[str, Any]] = ..., kw_only: bool = ...) -> Any
 
 [builtins fixtures/dataclasses.pyi]
 
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -659,9 +659,9 @@ a = None # type: A
 a.g()
 a.g(B())
 a.g(a) # E: No overload variant matches argument type "A" \
-       # N: Possible overload variant: \
-       # N:     def f(self, b: B) -> None \
-       # N:     <1 more non-matching overload not shown>
+       # N: Possible overload variants: \
+       # N:     def f(self) -> None \
+       # N:     def f(self, b: B) -> None
 
 [case testMethodAsDataAttributeInferredFromDynamicallyTypedMethod]
 
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -2079,9 +2079,9 @@ reveal_type(Base.make_some(1, 1))  # N: Revealed type is "builtins.tuple[__main_
 
 class Sub(Base[str]): ...
 Sub.make_some(1)  # E: No overload variant of "make_some" of "Base" matches argument type "int" \
-                  # N: Possible overload variant: \
+                  # N: Possible overload variants: \
                   # N:     def make_some(cls, item: str) -> Sub \
-                  # N:     <1 more non-matching overload not shown>
+                  # N:     def make_some(cls, item: str, n: int) -> Tuple[Sub, ...]
 [builtins fixtures/classmethod.pyi]
 
 [case testNoGenericAccessOnImplicitAttributes]
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -1547,9 +1547,9 @@ def f(blocks: Any): # E: Name "Any" is not defined \
 def f(blocks: object):
     to_process = []
     to_process = list(blocks) # E: No overload variant of "list" matches argument type "object" \
-                              # N: Possible overload variant: \
-                              # N:     def [T] __init__(self, x: Iterable[T]) -> List[T] \
-                              # N:     <1 more non-matching overload not shown>
+                              # N: Possible overload variants: \
+                              # N:     def [T] __init__(self) -> List[T] \
+                              # N:     def [T] __init__(self, x: Iterable[T]) -> List[T]
 [builtins fixtures/list.pyi]
 
 [case testInferListInitializedToEmptyAndAssigned]
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -525,21 +525,21 @@ if int():
 if int():
     b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B")
 f(b)     # E: No overload variant of "f" matches argument type "B" \
-         # N: Possible overload variant: \
+         # N: Possible overload variants: \
          # N:     def f(x: A) -> A \
-         # N:     <1 more non-matching overload not shown>
+         # N:     def f(x: B, y: A) -> B
 if int():
     b = f(b, a)
 if int():
     a = f(b, a) # E: Incompatible types in assignment (expression has type "B", variable has type "A")
 f(a, a)     # E: No overload variant of "f" matches argument types "A", "A" \
-            # N: Possible overload variant: \
-            # N:     def f(x: B, y: A) -> B \
-            # N:     <1 more non-matching overload not shown>
+            # N: Possible overload variants: \
+            # N:     def f(x: A) -> A \
+            # N:     def f(x: B, y: A) -> B
 f(b, b)     # E: No overload variant of "f" matches argument types "B", "B" \
-            # N: Possible overload variant: \
-            # N:     def f(x: B, y: A) -> B \
-            # N:     <1 more non-matching overload not shown>
+            # N: Possible overload variants: \
+            # N:     def f(x: A) -> A \
+            # N:     def f(x: B, y: A) -> B
 
 @overload
 def f(x: 'A') -> 'A': pass
@@ -1208,15 +1208,15 @@ def f(x: int, y: str) -> int: pass
 @overload
 def f(*x: str) -> str: pass
 f(*(1,))() # E: No overload variant of "f" matches argument type "Tuple[int]" \
-           # N: Possible overload variant: \
-           # N:     def f(*x: str) -> str \
-           # N:     <1 more non-matching overload not shown>
+           # N: Possible overload variants: \
+           # N:     def f(x: int, y: str) -> int \
+           # N:     def f(*x: str) -> str
 f(*('',))() # E: "str" not callable
 f(*(1, ''))() # E: "int" not callable
 f(*(1, '', 1))() # E: No overload variant of "f" matches argument type "Tuple[int, str, int]" \
-                 # N: Possible overload variant: \
-                 # N:     def f(*x: str) -> str \
-                 # N:     <1 more non-matching overload not shown>
+                 # N: Possible overload variants: \
+                 # N:     def f(x: int, y: str) -> int \
+                 # N:     def f(*x: str) -> str
 [builtins fixtures/tuple.pyi]
 
 [case testPreferExactSignatureMatchInOverload]
@@ -2502,9 +2502,9 @@ reveal_type(foo(*x))  # N: Revealed type is "__main__.C"
 y: List[str]
 foo(*y)  # E: No overload variant of "foo" matches argument type "List[str]" \
          # N: Possible overload variants: \
-         # N:     def foo(x: int, y: int, z: int, *args: int) -> C \
          # N:     def foo(x: int) -> A \
-         # N:     def foo(x: int, y: int) -> B
+         # N:     def foo(x: int, y: int) -> B \
+         # N:     def foo(x: int, y: int, z: int, *args: int) -> C
 [builtins fixtures/list.pyi]
 
 [case testOverloadMultipleVarargDefinition]
@@ -4822,7 +4822,9 @@ f(3)  # E: No overload variant of "f" matches argument type "int" \
       # N: Possible overload variants: \
       # N:     def f(x: A) -> None \
       # N:     def f(x: B) -> None \
-      # N:     <2 more similar overloads not shown, out of 5 total overloads>
+      # N:     def f(x: C) -> None \
+      # N:     def f(x: D) -> None \
+      # N:     def f(x: int, y: int) -> None
 
 @overload
 def g(x: A) -> None: ...
@@ -4888,9 +4890,9 @@ def f() -> None:
         pass
 
     g(str(), str())  # E: No overload variant of "g" matches argument types "str", "str" \
-                     # N: Possible overload variant: \
-                     # N:     def [T] g(x: T, y: int) -> T \
-                     # N:     <1 more non-matching overload not shown>
+                     # N: Possible overload variants: \
+                     # N:     def g(x: str) -> str \
+                     # N:     def [T] g(x: T, y: int) -> T
     reveal_type(g(str(), int()))  # N: Revealed type is "builtins.str*"
 [out]
 
@@ -4941,9 +4943,9 @@ reveal_type(attr("hi"))  # N: Revealed type is "builtins.int"
 x: Any
 reveal_type(attr(x)) # N: Revealed type is "Any"
 attr("hi", 1)  # E: No overload variant of "attr" matches argument types "str", "int" \
-               # N: Possible overload variant: \
+               # N: Possible overload variants: \
                # N:     def [T in (int, float)] attr(default: T = ..., blah: int = ...) -> T \
-               # N:     <1 more non-matching overload not shown>
+               # N:     def attr(default: Any = ...) -> int
 [file lib.pyi]
 from typing import overload, Any, TypeVar
 
@@ -4964,9 +4966,9 @@ reveal_type(attr("hi"))  # N: Revealed type is "builtins.int"
 x: Any
 reveal_type(attr(x)) # N: Revealed type is "Any"
 attr("hi", 1)  # E: No overload variant of "attr" matches argument types "str", "int" \
-               # N: Possible overload variant: \
+               # N: Possible overload variants: \
                # N:     def [T <: int] attr(default: T = ..., blah: int = ...) -> T \
-               # N:     <1 more non-matching overload not shown>
+               # N:     def attr(default: Any = ...) -> int
 [file lib.pyi]
 from typing import overload, TypeVar, Any
 
diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test
--- a/test-data/unit/check-protocols.test
+++ b/test-data/unit/check-protocols.test
@@ -2140,7 +2140,10 @@ main:18: note:         @overload
 main:18: note:         def f(self, x: int) -> int
 main:18: note:         @overload
 main:18: note:         def f(self, x: str) -> str
-main:18: note:         <2 more overloads not shown>
+main:18: note:         @overload
+main:18: note:         def f(self, x: C1) -> C2
+main:18: note:         @overload
+main:18: note:         def f(self, x: C2) -> C1
 main:18: note:     Got:
 main:18: note:         def f(self) -> None
 
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -7241,9 +7241,9 @@ class C:
 [out]
 ==
 a.py:2: error: No overload variant of "B" matches argument type "int"
-a.py:2: note: Possible overload variant:
+a.py:2: note: Possible overload variants:
 a.py:2: note:     def __init__(self, x: str) -> B
-a.py:2: note:     <1 more non-matching overload not shown>
+a.py:2: note:     def __init__(self, x: str, y: int) -> B
 
 [case testOverloadedToNormalMethodMetaclass]
 import a