From 746df041ffacf8a6c54727f7ea7a693c22a55486 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 9 Jan 2026 03:33:34 -0500 Subject: [PATCH 1/9] improve error message for overloaded function --- mypy/checkexpr.py | 2 +- mypy/messages.py | 51 +++++++++++++++++++++++++++ test-data/unit/check-expressions.test | 16 +++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9990caaeb7a1..f35473c995e4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2813,7 +2813,7 @@ def check_overload_call( code = None else: code = codes.OPERATOR - self.msg.no_variant_matches_arguments(callee, arg_types, context, code=code) + self.msg.no_variant_matches_arguments(callee, arg_types, context, arg_names=arg_names, arg_kinds=arg_kinds, code=code) result = self.check_call( target, diff --git a/mypy/messages.py b/mypy/messages.py index 1e589e1bdf04..6f6067da0741 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1085,14 +1085,65 @@ def no_variant_matches_arguments( arg_types: list[Type], context: Context, *, + arg_names: Sequence[str | None] | None, + arg_kinds: list[ArgKind] | None = None, code: ErrorCode | None = None, ) -> None: code = code or codes.CALL_OVERLOAD name = callable_name(overload) if name: name_str = f" of {name}" + for_func = f" for overloaded function {name}" else: name_str = "" + for_func = "" + + # For keyword argument errors + unexpected_kwargs: list[tuple[str, Type]] = [] + if arg_names is not None and arg_kinds is not None: + all_valid_kwargs: set[str] = set() + for item in overload.items: + for i, arg_name in enumerate(item.arg_names): + if arg_name is not None and arg_kinds[i] != ARG_STAR: + all_valid_kwargs.add(arg_name) + if item.is_kw_arg: + all_valid_kwargs.clear() + break + + if all_valid_kwargs: + for i, (arg_name, arg_kind) in enumerate(zip(arg_names, arg_kinds)): + if arg_kind == ARG_NAMED and arg_name is not None: + if arg_name not in all_valid_kwargs: + unexpected_kwargs.append((arg_name, arg_types[i])) + + if unexpected_kwargs: + for kwarg_name, kwarg_type in unexpected_kwargs: + matching_type_args: list[str] = [] + not_matching_type_args: list[str] = [] + + for item in overload.items: + for i, formal_type in enumerate(item.arg_types): + formal_name = item.arg_names[i] + if formal_name is not None and item.arg_kinds[i] != ARG_STAR: + if is_subtype(kwarg_type, formal_type): + if formal_name not in matching_type_args: + matching_type_args.append(formal_name) + else: + if formal_name not in not_matching_type_args: + not_matching_type_args.append(formal_name) + + matches = best_matches(kwarg_name, matching_type_args, n=3) + if not matches: + matches = best_matches(kwarg_name, not_matching_type_args, n=3) + + msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func + if matches: + msg += f"; did you mean {pretty_seq(matches, 'or')}?" + self.fail(msg, context, code=code) + + # do we want to still show possible overload variants as a note? + return + arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types) num_args = len(arg_types) if num_args == 0: diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 30b1f1a68e15..3dd0ae527f23 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2561,3 +2561,19 @@ def last_known_value() -> None: x, y, z = xy # E: Unpacking a string is disallowed reveal_type(z) # N: Revealed type is "builtins.str" [builtins fixtures/primitives.pyi] + +[case testInvalidArgumentInOverloadError] +from typing import overload + +@overload +def f(foobar: int) -> None: + pass + +@overload +def f(foobar: str) -> None: + pass + +def f(foobar: bool) -> None: + pass + +f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \ No newline at end of file From 61b0807754992f8f1081315891a8abbfbb453814 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 9 Jan 2026 03:48:29 -0500 Subject: [PATCH 2/9] added line number to error message --- mypy/messages.py | 18 ++++++++++++++++-- test-data/unit/check-expressions.test | 14 ++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 6f6067da0741..a1bad7abb75e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -44,6 +44,7 @@ ArgKind, CallExpr, Context, + Decorator, Expression, FuncDef, IndexExpr, @@ -1104,7 +1105,7 @@ def no_variant_matches_arguments( all_valid_kwargs: set[str] = set() for item in overload.items: for i, arg_name in enumerate(item.arg_names): - if arg_name is not None and arg_kinds[i] != ARG_STAR: + if arg_name is not None and item.arg_kinds[i] != ARG_STAR: all_valid_kwargs.add(arg_name) if item.is_kw_arg: all_valid_kwargs.clear() @@ -1120,28 +1121,41 @@ def no_variant_matches_arguments( for kwarg_name, kwarg_type in unexpected_kwargs: matching_type_args: list[str] = [] not_matching_type_args: list[str] = [] + matching_variant: CallableType | None = None for item in overload.items: + has_type_match = False for i, formal_type in enumerate(item.arg_types): formal_name = item.arg_names[i] if formal_name is not None and item.arg_kinds[i] != ARG_STAR: if is_subtype(kwarg_type, formal_type): if formal_name not in matching_type_args: matching_type_args.append(formal_name) + has_type_match = True else: if formal_name not in not_matching_type_args: not_matching_type_args.append(formal_name) + if has_type_match and matching_variant is None: + matching_variant = item matches = best_matches(kwarg_name, matching_type_args, n=3) if not matches: matches = best_matches(kwarg_name, not_matching_type_args, n=3) msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func + + if matching_variant is not None and matching_variant.definition is not None: + defn = matching_variant.definition + if isinstance(defn, Decorator): + func_line = defn.func.line + else: + func_line = defn.line + msg += f" defined on line {func_line}" + if matches: msg += f"; did you mean {pretty_seq(matches, 'or')}?" self.fail(msg, context, code=code) - # do we want to still show possible overload variants as a note? return arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 3dd0ae527f23..2978da1b0366 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2563,17 +2563,15 @@ def last_known_value() -> None: [builtins fixtures/primitives.pyi] [case testInvalidArgumentInOverloadError] -from typing import overload +from typing import overload, Union -@overload -def f(foobar: int) -> None: - pass +@overload +def f(foobar: int) -> None: ... @overload -def f(foobar: str) -> None: - pass +def f(foobar: str) -> None: ... -def f(foobar: bool) -> None: +def f(foobar: Union[int, str]) -> None: pass -f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \ No newline at end of file +f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" defined on line 4; did you mean "foobar"? \ No newline at end of file From 1beb07655f787d5fc7dcbca0e44faa5c70af4378 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 9 Jan 2026 04:02:10 -0500 Subject: [PATCH 3/9] added fallback to list of possible overload variants --- mypy/messages.py | 9 +++++++++ test-data/unit/check-expressions.test | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index a1bad7abb75e..5f292df83fa1 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1155,6 +1155,15 @@ def no_variant_matches_arguments( if matches: msg += f"; did you mean {pretty_seq(matches, 'or')}?" self.fail(msg, context, code=code) + + if matching_variant is None: + self.note( + f"Possible overload variant{plural_s(len(overload.items))}:", + context, + code=code, + ) + for item in overload.items: + self.note(pretty_callable(item, self.options), context, offset=4, code=code) return diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 2978da1b0366..ca21ed5e028f 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2574,4 +2574,8 @@ def f(foobar: str) -> None: ... def f(foobar: Union[int, str]) -> None: pass -f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" defined on line 4; did you mean "foobar"? \ No newline at end of file +f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" defined on line 4; did you mean "foobar"? +f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \ + # N: Possible overload variants: \ + # N: def f(foobar: int) -> None \ + # N: def f(foobar: str) -> None \ No newline at end of file From 54b33c4f25577baf3bb93ddbaa128989140c4c5d Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 16 Jan 2026 03:03:29 -0500 Subject: [PATCH 4/9] formatting --- mypy/messages.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 5f292df83fa1..621cc2405fbb 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1100,23 +1100,23 @@ def no_variant_matches_arguments( for_func = "" # For keyword argument errors - unexpected_kwargs: list[tuple[str, Type]] = [] - if arg_names is not None and arg_kinds is not None: + unexpected_kwargs: list[tuple[str, Type]] = [] + if arg_names is not None and arg_kinds is not None: all_valid_kwargs: set[str] = set() for item in overload.items: - for i, arg_name in enumerate(item.arg_names): + for i, arg_name in enumerate(item.arg_names): if arg_name is not None and item.arg_kinds[i] != ARG_STAR: all_valid_kwargs.add(arg_name) if item.is_kw_arg: all_valid_kwargs.clear() break - if all_valid_kwargs: + if all_valid_kwargs: for i, (arg_name, arg_kind) in enumerate(zip(arg_names, arg_kinds)): - if arg_kind == ARG_NAMED and arg_name is not None: + if arg_kind == ARG_NAMED and arg_name is not None: if arg_name not in all_valid_kwargs: unexpected_kwargs.append((arg_name, arg_types[i])) - + if unexpected_kwargs: for kwarg_name, kwarg_type in unexpected_kwargs: matching_type_args: list[str] = [] @@ -1137,13 +1137,13 @@ def no_variant_matches_arguments( not_matching_type_args.append(formal_name) if has_type_match and matching_variant is None: matching_variant = item - + matches = best_matches(kwarg_name, matching_type_args, n=3) if not matches: matches = best_matches(kwarg_name, not_matching_type_args, n=3) - + msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func - + if matching_variant is not None and matching_variant.definition is not None: defn = matching_variant.definition if isinstance(defn, Decorator): @@ -1151,11 +1151,11 @@ def no_variant_matches_arguments( else: func_line = defn.line msg += f" defined on line {func_line}" - - if matches: + + if matches: msg += f"; did you mean {pretty_seq(matches, 'or')}?" self.fail(msg, context, code=code) - + if matching_variant is None: self.note( f"Possible overload variant{plural_s(len(overload.items))}:", @@ -1165,8 +1165,8 @@ def no_variant_matches_arguments( for item in overload.items: self.note(pretty_callable(item, self.options), context, offset=4, code=code) - return - + return + arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types) num_args = len(arg_types) if num_args == 0: From 8aabd640b5be29de6a9b6a60745b8cc77405714d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:08:51 +0000 Subject: [PATCH 5/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checkexpr.py | 4 +++- mypy/messages.py | 4 +++- test-data/unit/check-expressions.test | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f35473c995e4..1ac66aa57654 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2813,7 +2813,9 @@ def check_overload_call( code = None else: code = codes.OPERATOR - self.msg.no_variant_matches_arguments(callee, arg_types, context, arg_names=arg_names, arg_kinds=arg_kinds, code=code) + self.msg.no_variant_matches_arguments( + callee, arg_types, context, arg_names=arg_names, arg_kinds=arg_kinds, code=code + ) result = self.check_call( target, diff --git a/mypy/messages.py b/mypy/messages.py index 621cc2405fbb..be2840580fcd 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1163,7 +1163,9 @@ def no_variant_matches_arguments( code=code, ) for item in overload.items: - self.note(pretty_callable(item, self.options), context, offset=4, code=code) + self.note( + pretty_callable(item, self.options), context, offset=4, code=code + ) return diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index ca21ed5e028f..4c4cf2982b5c 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2578,4 +2578,4 @@ f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \ # N: Possible overload variants: \ # N: def f(foobar: int) -> None \ - # N: def f(foobar: str) -> None \ No newline at end of file + # N: def f(foobar: str) -> None From 4cf0e6859accce09bc476720c93546c2827dc176 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Thu, 22 Jan 2026 21:58:11 -0500 Subject: [PATCH 6/9] revert line number change --- mypy/messages.py | 9 --------- mypy/semanal.py | 15 ++++++++------- test-data/unit/check-expressions.test | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index be2840580fcd..a300d7c0446c 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -44,7 +44,6 @@ ArgKind, CallExpr, Context, - Decorator, Expression, FuncDef, IndexExpr, @@ -1144,14 +1143,6 @@ def no_variant_matches_arguments( msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func - if matching_variant is not None and matching_variant.definition is not None: - defn = matching_variant.definition - if isinstance(defn, Decorator): - func_line = defn.func.line - else: - func_line = defn.line - msg += f" defined on line {func_line}" - if matches: msg += f"; did you mean {pretty_seq(matches, 'or')}?" self.fail(msg, context, code=code) diff --git a/mypy/semanal.py b/mypy/semanal.py index 11f0156372bf..20bcb2f4ac60 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -493,10 +493,6 @@ def __init__( # since it's possible that the name will be there once the namespace is complete. self.incomplete_namespaces = incomplete_namespaces self.all_exports: list[str] = [] - # Map from module id to list of explicitly exported names (i.e. names in __all__). - # This is used by stubgen/stubtest, DO NOT use for any other purposes as it is - # not populated on incremental runs (nor in parallel mode). - self.export_map: dict[str, list[str]] = {} self.plugin = plugin # If True, process function definitions. If False, don't. This is used # for processing module top levels in fine-grained incremental mode. @@ -724,7 +720,6 @@ def refresh_top_level(self, file_node: MypyFile) -> None: if file_node.fullname == "typing_extensions": self.add_typing_extension_aliases(file_node) self.adjust_public_exports() - self.export_map[self.cur_mod_id] = self.all_exports self.all_exports = [] def add_implicit_module_attrs(self, file_node: MypyFile) -> None: @@ -4061,7 +4056,11 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: type_params: TypeVarLikeList | None all_type_params_names: list[str] | None if self.check_type_alias_type_call(s.rvalue, name=lvalue.name): - rvalue = s.rvalue.args[1] + rvalue = ( + s.rvalue.args[1] + if s.rvalue.arg_kinds[1] == ARG_POS + else s.rvalue.args[s.rvalue.arg_names.index("value")] + ) pep_695 = True type_params, all_type_params_names = self.analyze_type_alias_type_params(s.rvalue) else: @@ -4249,7 +4248,9 @@ def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGu return False if not self.check_typevarlike_name(rvalue, name, rvalue): return False - if rvalue.arg_kinds.count(ARG_POS) != 2: + if rvalue.arg_kinds.count(ARG_POS) != ( + 2 - ("value" in rvalue.arg_names) - ("name" in rvalue.arg_names) + ): return False return True diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 4c4cf2982b5c..eb9c15b29fe7 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2574,7 +2574,7 @@ def f(foobar: str) -> None: ... def f(foobar: Union[int, str]) -> None: pass -f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" defined on line 4; did you mean "foobar"? +f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \ # N: Possible overload variants: \ # N: def f(foobar: int) -> None \ From a772b802b5b0106cae6df0d64109c40093a45950 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Thu, 22 Jan 2026 23:11:32 -0500 Subject: [PATCH 7/9] add tests --- test-data/unit/check-expressions.test | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index eb9c15b29fe7..2bbbd7a3992d 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2579,3 +2579,20 @@ f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded funct # N: Possible overload variants: \ # N: def f(foobar: int) -> None \ # N: def f(foobar: str) -> None + +f(fobar=1, baz=2) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \ + # E: Unexpected keyword argument "baz" for overloaded function "f" + +f(foobar=1, invalid=2) # E: Unexpected keyword argument "invalid" for overloaded function "f" + +@overload +def g(x: int, y: int) -> int: ... + +@overload +def g(x: str, y: str) -> str: ... + +def g(x: Union[int, str], y: Union[int, str]) -> Union[int, str]: + return x + +g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g" +[builtins fixtures/list.pyi] From a91ff26a71458421ab77e0ac4cd1a65cc68a797e Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 23 Jan 2026 11:14:58 -0500 Subject: [PATCH 8/9] merge kwargs without suggestions into one error line --- mypy/messages.py | 70 +++++++++++++++++++-------- test-data/unit/check-expressions.test | 35 ++++++++++++-- 2 files changed, 80 insertions(+), 25 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index a300d7c0446c..4f5e6bb96c56 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1117,48 +1117,78 @@ def no_variant_matches_arguments( unexpected_kwargs.append((arg_name, arg_types[i])) if unexpected_kwargs: + all_kwargs_confident = True + kwargs_with_suggestions: list[tuple[str, list[str]]] = [] + kwargs_without_suggestions: list[str] = [] + + # Find suggestions for each unexpected kwarg, prioritizing type-matching args for kwarg_name, kwarg_type in unexpected_kwargs: matching_type_args: list[str] = [] not_matching_type_args: list[str] = [] - matching_variant: CallableType | None = None + has_matching_variant = False for item in overload.items: - has_type_match = False + item_has_type_match = False for i, formal_type in enumerate(item.arg_types): formal_name = item.arg_names[i] if formal_name is not None and item.arg_kinds[i] != ARG_STAR: if is_subtype(kwarg_type, formal_type): if formal_name not in matching_type_args: matching_type_args.append(formal_name) - has_type_match = True - else: - if formal_name not in not_matching_type_args: - not_matching_type_args.append(formal_name) - if has_type_match and matching_variant is None: - matching_variant = item + item_has_type_match = True + elif formal_name not in not_matching_type_args: + not_matching_type_args.append(formal_name) + if item_has_type_match: + has_matching_variant = True matches = best_matches(kwarg_name, matching_type_args, n=3) if not matches: matches = best_matches(kwarg_name, not_matching_type_args, n=3) - msg = f'Unexpected keyword argument "{kwarg_name}"' + for_func - if matches: - msg += f"; did you mean {pretty_seq(matches, 'or')}?" - self.fail(msg, context, code=code) + kwargs_with_suggestions.append((kwarg_name, matches)) + else: + kwargs_without_suggestions.append(kwarg_name) - if matching_variant is None: - self.note( - f"Possible overload variant{plural_s(len(overload.items))}:", + if not has_matching_variant: + all_kwargs_confident = False + + for kwarg_name, matches in kwargs_with_suggestions: + self.fail( + f'Unexpected keyword argument "{kwarg_name}"' + f"{for_func}; did you mean {pretty_seq(matches, 'or')}?", + context, + code=code, + ) + + if kwargs_without_suggestions: + if len(kwargs_without_suggestions) == 1: + self.fail( + f'Unexpected keyword argument "{kwargs_without_suggestions[0]}"{for_func}', + context, + code=code, + ) + else: + quoted_names = ", ".join(f'"{n}"' for n in kwargs_without_suggestions) + self.fail( + f"Unexpected keyword arguments {quoted_names}{for_func}", context, code=code, ) - for item in overload.items: - self.note( - pretty_callable(item, self.options), context, offset=4, code=code - ) - return + if not all_kwargs_confident: + self.note( + f"Possible overload variant{plural_s(len(overload.items))}:", + context, + code=code, + ) + for item in overload.items: + self.note( + pretty_callable(item, self.options), context, offset=4, code=code + ) + + if all_kwargs_confident and len(unexpected_kwargs) == len(arg_types): + return arg_types_str = ", ".join(format_type(arg, self.options) for arg in arg_types) num_args = len(arg_types) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 2bbbd7a3992d..cee7f0bc8160 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -2571,19 +2571,23 @@ def f(foobar: int) -> None: ... @overload def f(foobar: str) -> None: ... -def f(foobar: Union[int, str]) -> None: - pass +def f(foobar: Union[int, str]) -> None: pass f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? f(random=[1,2,3]) # E: Unexpected keyword argument "random" for overloaded function "f" \ # N: Possible overload variants: \ # N: def f(foobar: int) -> None \ - # N: def f(foobar: str) -> None + # N: def f(foobar: str) -> None \ + # E: No overload variant of "f" matches argument type "list[int]" f(fobar=1, baz=2) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \ # E: Unexpected keyword argument "baz" for overloaded function "f" -f(foobar=1, invalid=2) # E: Unexpected keyword argument "invalid" for overloaded function "f" +f(foobar=1, a=2, b=3, c=4, d=5, e=6) # E: Unexpected keyword arguments "a", "b", "c", "d", "e" for overloaded function "f" \ + # E: No overload variant of "f" matches argument types "int", "int", "int", "int", "int", "int" \ + # N: Possible overload variants: \ + # N: def f(foobar: int) -> None \ + # N: def f(foobar: str) -> None @overload def g(x: int, y: int) -> int: ... @@ -2594,5 +2598,26 @@ def g(x: str, y: str) -> str: ... def g(x: Union[int, str], y: Union[int, str]) -> Union[int, str]: return x -g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g" +f(fobar=1, other=[1,2,3]) # E: Unexpected keyword argument "fobar" for overloaded function "f"; did you mean "foobar"? \ + # E: Unexpected keyword argument "other" for overloaded function "f" \ + # N: Possible overload variants: \ + # N: def f(foobar: int) -> None \ + # N: def f(foobar: str) -> None \ + # E: No overload variant of "f" matches argument types "int", "list[int]" + +g([1, 2], 3) # E: No overload variant of "g" matches argument types "list[int]", "int" \ + # N: Possible overload variants: \ + # N: def g(x: int, y: int) -> int \ + # N: def g(x: str, y: str) -> str + +g([1, 2], z=3) # E: Unexpected keyword argument "z" for overloaded function "g" \ + # E: No overload variant of "g" matches argument types "list[int]", "int" \ + # N: Possible overload variants: \ + # N: def g(x: int, y: int) -> int \ + # N: def g(x: str, y: str) -> str + +g(x="hello", y=1) # E: No overload variant of "g" matches argument types "str", "int" \ + # N: Possible overload variants: \ + # N: def g(x: int, y: int) -> int \ + # N: def g(x: str, y: str) -> str [builtins fixtures/list.pyi] From 08e94c3ec206b328e2d1a42d1eef7ea9fcec2198 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:16:49 +0000 Subject: [PATCH 9/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/messages.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 4f5e6bb96c56..4130520b5054 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1183,9 +1183,7 @@ def no_variant_matches_arguments( code=code, ) for item in overload.items: - self.note( - pretty_callable(item, self.options), context, offset=4, code=code - ) + self.note(pretty_callable(item, self.options), context, offset=4, code=code) if all_kwargs_confident and len(unexpected_kwargs) == len(arg_types): return