From 46be305ad243a5286d4269b1f8e5fd67623d38c2 Mon Sep 17 00:00:00 2001 From: InSync Date: Thu, 15 May 2025 09:51:23 +0700 Subject: [PATCH] [ty] Include synthesized arguments in displayed counts for `too-many-positional-arguments` (#18098) ## Summary Resolves [#290](https://github.com/astral-sh/ty/issues/290). All arguments, synthesized or not, are now accounted for in `too-many-positional-arguments`'s error message. For example, consider this example: ```python class C: def foo(self): ... C().foo(1) # !!! ``` Previously, ty would say: > Too many positional arguments to bound method foo: expected 0, got 1 After this change, it will say: > Too many positional arguments to bound method foo: expected 1, got 2 This is what Python itself does too: ```text Traceback (most recent call last): File "", line 3, in C().foo() ~~~~~~~^^ TypeError: C.foo() takes 0 positional arguments but 1 was given ``` ## Test Plan Markdown tests. --- .../resources/mdtest/call/constructor.md | 24 +++++++++---------- .../resources/mdtest/call/subclass_of.md | 2 +- .../ty_python_semantic/src/types/call/bind.rs | 10 ++------ 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/call/constructor.md b/crates/ty_python_semantic/resources/mdtest/call/constructor.md index e56fb09320..0e89cb5d32 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/constructor.md +++ b/crates/ty_python_semantic/resources/mdtest/call/constructor.md @@ -47,7 +47,7 @@ class Foo: ... reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 0, got 1" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" reveal_type(Foo(1)) # revealed: Foo ``` @@ -62,7 +62,7 @@ reveal_type(Foo(1)) # revealed: Foo # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2" +# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -84,7 +84,7 @@ reveal_type(Foo(1)) # revealed: Foo # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2" +# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -105,7 +105,7 @@ def _(flag: bool) -> None: # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" reveal_type(Foo()) # revealed: Foo - # error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2" + # error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -198,7 +198,7 @@ reveal_type(Foo(1)) # revealed: Foo # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -217,7 +217,7 @@ reveal_type(Foo(1)) # revealed: Foo # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -238,7 +238,7 @@ def _(flag: bool) -> None: # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(Foo()) # revealed: Foo - # error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" + # error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -344,7 +344,7 @@ class Foo: reveal_type(Foo()) # revealed: Foo reveal_type(Foo(1)) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 2, got 3" reveal_type(Foo(1, 2)) # revealed: Foo ``` @@ -363,7 +363,7 @@ class Foo: # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(Foo()) # revealed: Foo -# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 0, got 1" +# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2" reveal_type(Foo(42)) # revealed: Foo class Foo2: @@ -376,7 +376,7 @@ class Foo2: # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" reveal_type(Foo2()) # revealed: Foo2 -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 0, got 1" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" reveal_type(Foo2(42)) # revealed: Foo2 class Foo3(metaclass=abc.ABCMeta): @@ -389,7 +389,7 @@ class Foo3(metaclass=abc.ABCMeta): # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(Foo3()) # revealed: Foo3 -# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 0, got 1" +# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2" reveal_type(Foo3(42)) # revealed: Foo3 class Foo4(metaclass=abc.ABCMeta): @@ -402,7 +402,7 @@ class Foo4(metaclass=abc.ABCMeta): # error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`" reveal_type(Foo4()) # revealed: Foo4 -# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 0, got 1" +# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" reveal_type(Foo4(42)) # revealed: Foo4 ``` diff --git a/crates/ty_python_semantic/resources/mdtest/call/subclass_of.md b/crates/ty_python_semantic/resources/mdtest/call/subclass_of.md index 49b0967098..544c4c7c90 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/subclass_of.md +++ b/crates/ty_python_semantic/resources/mdtest/call/subclass_of.md @@ -24,7 +24,7 @@ def _(subclass_of_c: type[C]): reveal_type(subclass_of_c("a")) # revealed: C # error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`" reveal_type(subclass_of_c()) # revealed: C - # error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2" + # error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 2, got 3" reveal_type(subclass_of_c(1, 2)) # revealed: C ``` diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index de8cc4168e..52189b9602 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -1285,14 +1285,8 @@ impl<'db> Binding<'db> { first_excess_argument_index, num_synthetic_args, ), - expected_positional_count: parameters - .positional() - .count() - // using saturating_sub to avoid negative values due to invalid syntax in source code - .saturating_sub(num_synthetic_args), - provided_positional_count: next_positional - // using saturating_sub to avoid negative values due to invalid syntax in source code - .saturating_sub(num_synthetic_args), + expected_positional_count: parameters.positional().count(), + provided_positional_count: next_positional, }); } let mut missing = vec![];