Skip to content

Always complain about invalid varargs and varkwargs #18207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2481,6 +2481,17 @@ 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])

Expand Down Expand Up @@ -2573,15 +2584,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,
Expand Down
2 changes: 1 addition & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion test-data/unit/check-functools.test
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,16 @@ 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: ...
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]
Expand Down
3 changes: 3 additions & 0 deletions test-data/unit/check-kwargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prior false negative

[builtins fixtures/dict.pyi]

[case testPassingKeywordVarArgsToNonVarArgsFunction]
Expand Down
16 changes: 9 additions & 7 deletions test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -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(*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: Expected iterable as variadic argument
f(a, *4) # E: Expected iterable as variadic argument
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prior false negative

[builtins fixtures/tuple.pyi]


Expand Down Expand Up @@ -543,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)
Expand Down Expand Up @@ -737,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
Expand Down