From 50fdc080e81fdc464e3a1c8331edcdbb685f9ef2 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Thu, 28 Nov 2024 13:38:27 -0800 Subject: [PATCH 1/4] regression test --- test-data/unit/check-varargs.test | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 2495a883aa71..5145b49c70a7 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -268,17 +268,19 @@ f(a, *(b, cc)) [builtins fixtures/tuple.pyi] [case testInvalidVarArg] -# flags: --no-strict-optional def f(a: 'A') -> None: pass class A: pass -a = None # type: A +a = A() -f(*None) +f(*None) # E: List or tuple expected as variadic arguments f(*a) # E: List or tuple expected as variadic arguments f(*(a,)) + +f(*4) # E: List or tuple expected as variadic arguments +f(a, *4) [builtins fixtures/tuple.pyi] From 1880fdd99b3161f4ac508d898a98c9ce2632ea7e Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Thu, 28 Nov 2024 13:39:01 -0800 Subject: [PATCH 2/4] fix --- mypy/checkexpr.py | 22 +++++++++++++--------- mypy/messages.py | 2 +- test-data/unit/check-functools.test | 4 +++- test-data/unit/check-varargs.test | 14 +++++++------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 577576a4e5f8..02c802c7d6ff 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2481,6 +2481,19 @@ def check_argument_types( check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. mapper = ArgTypeExpander(self.argument_infer_context()) + + for arg_type, arg_kind in zip(arg_types, arg_kinds): + arg_type = get_proper_type(arg_type) + if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type): + self.msg.invalid_var_arg(arg_type, context) + if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg( + arg_type + ): + is_mapping = is_subtype( + arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") + ) + self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context) + for i, actuals in enumerate(formal_to_actual): orig_callee_arg_type = get_proper_type(callee.arg_types[i]) @@ -2573,15 +2586,6 @@ def check_argument_types( if actual_type is None: continue # Some kind of error was already reported. # Check that a *arg is valid as varargs. - if actual_kind == nodes.ARG_STAR and not self.is_valid_var_arg(actual_type): - self.msg.invalid_var_arg(actual_type, context) - if actual_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg( - actual_type - ): - is_mapping = is_subtype( - actual_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") - ) - self.msg.invalid_keyword_var_arg(actual_type, is_mapping, context) expanded_actual = mapper.expand_actual_type( actual_type, actual_kind, diff --git a/mypy/messages.py b/mypy/messages.py index d63df92c80a7..3ec1574827eb 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1394,7 +1394,7 @@ def could_not_infer_type_arguments( self.fail("Cannot infer function type argument", context) def invalid_var_arg(self, typ: Type, context: Context) -> None: - self.fail("List or tuple expected as variadic arguments", context) + self.fail("Expected iterable as variadic argument", context) def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) -> None: typ = get_proper_type(typ) diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index ea98a902d14b..c1868b3e3d72 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -215,7 +215,7 @@ def bar(*a: bytes, **k: int): p1("a", d="a", **k) p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str" p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str" - p1(*a) # E: List or tuple expected as variadic arguments + p1(*a) # E: Expected iterable as variadic argument def baz(a: int, b: int) -> int: ... @@ -223,6 +223,8 @@ def test_baz(xs: List[int]): p3 = functools.partial(baz, *xs) p3() p3(1) # E: Too many arguments for "baz" + + [builtins fixtures/dict.pyi] [case testFunctoolsPartialGeneric] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 5145b49c70a7..bb0e80acee1e 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -275,12 +275,12 @@ class A: pass a = A() -f(*None) # E: List or tuple expected as variadic arguments -f(*a) # E: List or tuple expected as variadic arguments +f(*None) # E: Expected iterable as variadic argument +f(*a) # E: Expected iterable as variadic argument f(*(a,)) -f(*4) # E: List or tuple expected as variadic arguments -f(a, *4) +f(*4) # E: Expected iterable as variadic argument +f(a, *4) # E: Expected iterable as variadic argument [builtins fixtures/tuple.pyi] @@ -545,9 +545,9 @@ if int(): if int(): b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" if int(): - a, b = f(a, *a) # E: List or tuple expected as variadic arguments + a, b = f(a, *a) # E: Expected iterable as variadic argument if int(): - a, b = f(*a) # E: List or tuple expected as variadic arguments + a, b = f(*a) # E: Expected iterable as variadic argument if int(): a, a = f(*aa) @@ -739,7 +739,7 @@ bar(*good1) bar(*good2) bar(*good3) bar(*bad1) # E: Argument 1 to "bar" has incompatible type "*I[str]"; expected "float" -bar(*bad2) # E: List or tuple expected as variadic arguments +bar(*bad2) # E: Expected iterable as variadic argument [builtins fixtures/dict.pyi] -- Keyword arguments unpacking From f5ae3a7ca5b467cf4958af62daa26f462da2915d Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Thu, 28 Nov 2024 13:41:39 -0800 Subject: [PATCH 3/4] varkwargs test --- test-data/unit/check-kwargs.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-data/unit/check-kwargs.test b/test-data/unit/check-kwargs.test index 4beac047e278..3a8c7f5ba454 100644 --- a/test-data/unit/check-kwargs.test +++ b/test-data/unit/check-kwargs.test @@ -358,6 +358,9 @@ f(**d) # E: Keywords must be strings f(**A()) # E: Argument after ** must be a mapping, not "A" kwargs: Optional[Any] f(**kwargs) # E: Argument after ** must be a mapping, not "Optional[Any]" + +def g(a: int) -> None: pass +g(a=1, **4) # E: Argument after ** must be a mapping, not "int" [builtins fixtures/dict.pyi] [case testPassingKeywordVarArgsToNonVarArgsFunction] From 880c9c0b5001c6c8fdadef1fee85bc5121991cb4 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Thu, 28 Nov 2024 13:41:55 -0800 Subject: [PATCH 4/4] format --- mypy/checkexpr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 02c802c7d6ff..073a5fa811ec 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2486,9 +2486,7 @@ def check_argument_types( arg_type = get_proper_type(arg_type) if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type): self.msg.invalid_var_arg(arg_type, context) - if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg( - arg_type - ): + if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type): is_mapping = is_subtype( arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") )