diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 603acbe84f0f..5534fb7e586a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2389,14 +2389,26 @@ def check_argument_count( ) # Check for too many or few values for formals. + missing_contiguous_pos_block: list[int] = [] + seen_kw = False for i, kind in enumerate(callee.arg_kinds): mapped_args = formal_to_actual[i] + seen_kw = ( + seen_kw + or kind == ArgKind.ARG_NAMED_OPT + or any(actual_kinds[k].is_named(star=True) for k in mapped_args) + ) if kind.is_required() and not mapped_args and not is_unexpected_arg_error: # No actual for a mandatory formal - if kind.is_positional(): - self.msg.too_few_arguments(callee, context, actual_names) - if object_type and callable_name and "." in callable_name: - self.missing_classvar_callable_note(object_type, callable_name, context) + if ( + kind.is_positional() + and not seen_kw + and ( + not missing_contiguous_pos_block + or missing_contiguous_pos_block[-1] == i - 1 + ) + ): + missing_contiguous_pos_block.append(i) else: argname = callee.arg_names[i] or "?" self.msg.missing_named_argument(callee, context, argname) @@ -2432,6 +2444,21 @@ def check_argument_count( if actual_kinds[mapped_args[0]] == nodes.ARG_STAR2 and paramspec_entries > 1: self.msg.fail("ParamSpec.kwargs should only be passed once", context) ok = False + if missing_contiguous_pos_block: + # To generate a correct message, expand kwargs manually. If a name was + # missing from the call but doesn't belong to continuous positional prefix, + # it was already reported as a missing kwarg. All args before first prefix + # item are guaranteed to have been passed positionally. + passed_or_reported_names = [ + name + for name in callee.arg_names[missing_contiguous_pos_block[-1] + 1 :] + # None may be there if it was an optional posonly param + if name is not None + ] + names_to_use = [None] * missing_contiguous_pos_block[0] + passed_or_reported_names + self.msg.too_few_arguments(callee, context, names_to_use) + if object_type and callable_name and "." in callable_name: + self.missing_classvar_callable_note(object_type, callable_name, context) return ok def check_for_extra_actual_arguments( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index bf6c51e86446..981d2d8520ec 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2008,7 +2008,7 @@ class D: def __set__(self, inst, v, other): pass class A: f = D() -A().f = 'x' # E: Too few arguments for "__set__" +A().f = 'x' # E: Missing positional argument "other" in call to "__set__" [case testDescriptorDunderSetWrongArgTypes] class D: @@ -2036,7 +2036,7 @@ class D: def __get__(self, inst, own, other): pass class A: f = D() -A().f = 'x' # E: Too few arguments for "__get__" +A().f = 'x' # E: Missing positional argument "other" in call to "__get__" [case testDescriptorDunderGetWrongArgTypeForInstance] from typing import Any diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 2ead202bd6af..8c07a09c443d 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -425,10 +425,11 @@ class Application: name: str = 'Unnamed' rating: int = field(kw_only=False) # E: Attributes without a default cannot follow attributes with one +reveal_type(Application) # N: Revealed type is "def (name: builtins.str =, rating: builtins.int) -> __main__.Application" Application(name='name', rating=5) Application('name', 123) Application('name', rating=123) -Application() # E: Missing positional argument "name" in call to "Application" +Application() # E: Too few arguments for "Application" Application('name') # E: Too few arguments for "Application" [builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index d6e3366401dd..9f573b581d00 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -242,7 +242,8 @@ g() # E: Missing named argument "x" for "g" [call-arg] def h(x: int, y: int, z: int) -> None: pass h(y=1, z=1) # E: Missing positional argument "x" in call to "h" [call-arg] -h(y=1) # E: Missing positional arguments "x", "z" in call to "h" [call-arg] +h(y=1) # E: Missing named argument "z" for "h" [call-arg] \ + # E: Missing positional argument "x" in call to "h" [call-arg] [case testErrorCodeArgType] def f(x: int) -> None: pass diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index ceb7af433dce..880a9705e4d9 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2467,6 +2467,79 @@ f(b=4) # E: Missing positional argument "a" in call to "f" def f(a, b, c, d=None) -> None: pass f(1, d=3) # E: Missing positional arguments "b", "c" in call to "f" +[case testMissingArgumentsErrorFromTypedDict] +from typing import Optional,TypedDict + +class DA(TypedDict): + a: int +class DB(TypedDict): + b: int +class DC(TypedDict): + c: int + +class DAB(DA, DB): pass +class DAC(DA, DC): pass +class DBC(DB, DC): pass +class DABC(DA, DB, DC): pass + +da: DA +db: DB +dc: DC +dab: DAB +dac: DAC +dbc: DBC +dabc: DABC + +def f(a: int, b: int, c: int, d: Optional[int] = None) -> None: pass + +f(**da) # E: Missing named argument "b" for "f" \ + # E: Missing named argument "c" for "f" +f(**db) # E: Missing named argument "c" for "f" \ + # E: Missing positional argument "a" in call to "f" +f(**dc) # E: Missing positional arguments "a", "b" in call to "f" +f(**dab) # E: Missing named argument "c" for "f" +f(**dac) # E: Missing named argument "b" for "f" +f(**dbc) # E: Missing positional argument "a" in call to "f" +f(**dabc) + +def g(a: int, /, b: int, c: int) -> None: pass + +g(**da) # E: Extra argument "a" from **args for "g" +g(0, **da) # E: Extra argument "a" from **args for "g" +g(**db) # E: Missing named argument "c" for "g" \ + # E: Too few arguments for "g" +g(0, **db) # E: Missing named argument "c" for "g" +g(**dc) # E: Too few arguments for "g" +g(0, **dc) # E: Missing positional argument "b" in call to "g" +g(**dab) # E: Extra argument "a" from **args for "g" +g(0, **dab) # E: Extra argument "a" from **args for "g" +g(**dac) # E: Extra argument "a" from **args for "g" +g(0, **dac) # E: Extra argument "a" from **args for "g" +g(**dbc) # E: Too few arguments for "g" +g(0, **dbc) +g(**dabc) # E: Extra argument "a" from **args for "g" + +def h(a: int, b: int, /, c: int) -> None: pass + +h(**da) # E: Extra argument "a" from **args for "h" +h(0, **da) # E: Extra argument "a" from **args for "h" +h(0, 1, **da) # E: Extra argument "a" from **args for "h" +h(**db) # E: Extra argument "b" from **args for "h" +h(0, **db) # E: Extra argument "b" from **args for "h" +h(0, 1, **db) # E: Extra argument "b" from **args for "h" +h(**dc) # E: Too few arguments for "h" +h(0, **dc) # E: Too few arguments for "h" +h(0, 1, **dc) +h(**dab) # E: Extra argument "b" from **args for "h" +h(0, **dab) # E: Extra argument "b" from **args for "h" +h(**dac) # E: Extra argument "a" from **args for "h" +h(0, **dac) # E: Extra argument "a" from **args for "h" +h(**dbc) # E: Extra argument "b" from **args for "h" +h(0, **dbc) # E: Extra argument "b" from **args for "h" +h(**dabc) # E: Extra argument "b" from **args for "h" +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testReturnTypeLineNumberWithDecorator] def dec(f): pass diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index fa2cacda275d..2901eebb8eac 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -495,9 +495,9 @@ def main5(**d2: Unpack[D2]) -> None: partial(fn2, **d2)() # E: Extra argument "a2" from **args for "fn2" def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None: - partial(fn3, **d1)() # E: Missing positional argument "a1" in call to "fn3" + partial(fn3, **d1)() # E: Missing named argument "a2" for "fn3" partial(fn3, **d1)("asdf") # E: Too many positional arguments for "fn3" \ - # E: Too few arguments for "fn3" \ + # E: Missing named argument "a2" for "fn3" \ # E: Argument 1 to "fn3" has incompatible type "str"; expected "int" partial(fn3, **d1)(a2="asdf") partial(fn3, **d1)(**a2good) @@ -530,7 +530,7 @@ reveal_type(first_kw(args=[1])) # N: Revealed type is "builtins.int" # TODO: this is indeed invalid, but the error is incomprehensible. first_kw([1]) # E: Too many positional arguments for "get" \ - # E: Too few arguments for "get" \ + # E: Missing named argument "args" for "get" \ # E: Argument 1 to "get" has incompatible type "list[int]"; expected "int" [builtins fixtures/list.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index bfd6334b5077..a0b9f933b656 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -889,7 +889,7 @@ def h(x: int, y: str) -> None: pass g(h, 1, y='x') g(h, 1, x=1) # E: "g" gets multiple values for keyword argument "x" \ - # E: Missing positional argument "y" in call to "g" + # E: Missing named argument "y" for "g" class C[**P, T]: def m(self, *args: P.args, **kwargs: P.kwargs) -> T: ... diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index dd3f793fd02b..4f409642a957 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -154,7 +154,7 @@ f(0, 0, kw=0) f(0, p_or_kw=0, kw=0) f(p=0, p_or_kw=0, kw=0) # E: Unexpected keyword argument "p" for "f" f(0, **d) -f(**d) # E: Missing positional argument "p_or_kw" in call to "f" +f(**d) # E: Too few arguments for "f" [builtins fixtures/dict.pyi] [case testPEP570Signatures1] diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 9ab68b32472d..6e64e299bfa8 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -453,9 +453,9 @@ if object(): if object(): raise MyErrorWithDefault if object(): - raise MyBaseError # E: Too few arguments for "MyBaseError" + raise MyBaseError # E: Missing positional argument "required" in call to "MyBaseError" if object(): - raise MyError # E: Too few arguments for "MyError" + raise MyError # E: Missing positional arguments "required1", "required2" in call to "MyError" if object(): raise MyKwError # E: Missing named argument "kwonly" for "MyKwError" [builtins fixtures/exception.pyi] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index a068a63274ca..2290eae8ba39 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1596,7 +1596,7 @@ f1(**a) f2(**a) # E: Argument "y" to "f2" has incompatible type "str"; expected "int" f3(**a) # E: Argument "x" to "f3" has incompatible type "int"; expected "B" f4(**a) # E: Extra argument "y" from **args for "f4" -f5(**a) # E: Missing positional arguments "y", "z" in call to "f5" +f5(**a) # E: Missing named argument "z" for "f5" f6(**a) # E: Extra argument "y" from **args for "f6" f1(1, **a) # E: "f1" gets multiple values for keyword argument "x" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 680021a166f2..0f7a4bfb96af 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -266,7 +266,7 @@ f(*(a, b, b)) # E: Argument 1 to "f" has incompatible type "*tuple[A, B, B]"; ex f(*(b, b, c)) # E: Argument 1 to "f" has incompatible type "*tuple[B, B, C]"; expected "A" f(a, *(b, b)) # E: Argument 2 to "f" has incompatible type "*tuple[B, B]"; expected "C" f(b, *(b, c)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" -f(*(a, b)) # E: Missing positional arguments "b", "c" in call to "f" +f(*(a, b)) # E: Missing positional argument "c" in call to "f" f(*(a, b, c, c)) # E: Too many arguments for "f" f(a, *(b, c, c)) # E: Too many arguments for "f" f(*(a, b, c)) @@ -342,7 +342,7 @@ f(b, *(b, b)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(b, b, *(b,)) # E: Argument 1 to "f" has incompatible type "B"; expected "A" f(a, a, *(b,)) # E: Argument 2 to "f" has incompatible type "A"; expected "B" f(a, b, *(a,)) # E: Argument 3 to "f" has incompatible type "*tuple[A]"; expected "B" -f(*()) # E: Too few arguments for "f" +f(*()) # E: Missing positional argument "a" in call to "f" f(*(a, b, b)) f(a, *(b, b)) f(a, b, *(b,)) @@ -400,7 +400,7 @@ class A: pass class B: pass a, b = None, None # type: (A, B) -f(*()) # E: Too few arguments for "f" +f(*()) # E: Missing positional argument "a" in call to "f" f(a, *[a]) # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "Optional[B]" \ # E: Argument 2 to "f" has incompatible type "*list[A]"; expected "B" f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*list[A]"; expected "B"
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: