Repository: pylint-dev/pylint

Issue:
False negatives for `duplicate-argument-name`
### Bug description

Currently only positional-or-keyword and keyword-only arguments are taken into account for this check:

```python

def foo1(_, _): # [duplicate-argument-name]
...

def foo3(_, _=3): # [duplicate-argument-name]
...

def foo4(_, *, _): # [duplicate-argument-name]
...
```


positional-only, *varargs and **kwargs are not considered currently:

```python
def foo5(_, *_):
    """*vararg"""

def foo6(_, /, _):
    """positional-only"""

def foo7(_, **_):
    """**kwargs"""
```


### Configuration

_No response_

### Command used

```shell
pylint a.py
```


### Pylint output

```python
The message is not emitted because of the false negatives.
```


### Expected behavior

E0108: Duplicate argument name

### Pylint version

```shell
pylint 3.3.0-dev0
astroid 3.3.0-dev0
Python 3.12.0
```


### OS / Environment

_No response_

### Additional dependencies

_No response_


Discussion:

Patch:
diff --git a/doc/user_guide/checkers/features.rst b/doc/user_guide/checkers/features.rst
index cb63930a01..786495754f 100644
--- a/doc/user_guide/checkers/features.rst
+++ b/doc/user_guide/checkers/features.rst
@@ -65,7 +65,7 @@ Basic checker Messages
   methods and is instantiated.
 :star-needs-assignment-target (E0114): *Can use starred expression only in assignment target*
   Emitted when a star expression is not used in an assignment target.
-:duplicate-argument-name (E0108): *Duplicate argument name %s in function definition*
+:duplicate-argument-name (E0108): *Duplicate argument name %r in function definition*
   Duplicate argument names in function definitions are syntax errors.
 :return-in-init (E0101): *Explicit return in __init__*
   Used when the special class method __init__ has an explicit return value.
diff --git a/doc/whatsnew/fragments/9669.false_negative b/doc/whatsnew/fragments/9669.false_negative
new file mode 100644
index 0000000000..483de2a77d
--- /dev/null
+++ b/doc/whatsnew/fragments/9669.false_negative
@@ -0,0 +1,3 @@
+Fix a false negative for ``duplicate-argument-name`` by including ``positional-only``, ``*args`` and ``**kwargs`` arguments in the check.
+
+Closes #9669
diff --git a/pylint/checkers/base/basic_error_checker.py b/pylint/checkers/base/basic_error_checker.py
index e58be23096..d6e10f31d0 100644
--- a/pylint/checkers/base/basic_error_checker.py
+++ b/pylint/checkers/base/basic_error_checker.py
@@ -7,8 +7,6 @@
 from __future__ import annotations
 
 import itertools
-from collections.abc import Iterator
-from typing import Any
 
 import astroid
 from astroid import nodes
@@ -145,7 +143,7 @@ class BasicErrorChecker(_BasicChecker):
             "pre-decrement operator -- and ++, which doesn't exist in Python.",
         ),
         "E0108": (
-            "Duplicate argument name %s in function definition",
+            "Duplicate argument name %r in function definition",
             "duplicate-argument-name",
             "Duplicate argument names in function definitions are syntax errors.",
         ),
@@ -285,8 +283,7 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
                     self.add_message("return-in-init", node=node)
         # Check for duplicate names by clustering args with same name for detailed report
         arg_clusters = {}
-        arguments: Iterator[Any] = filter(None, [node.args.args, node.args.kwonlyargs])
-        for arg in itertools.chain.from_iterable(arguments):
+        for arg in node.args.arguments:
             if arg.name in arg_clusters:
                 self.add_message(
                     "duplicate-argument-name",


Test patch:
diff --git a/tests/functional/a/async_functions.txt b/tests/functional/a/async_functions.txt
index 985fddec2d..bfb9e52021 100644
--- a/tests/functional/a/async_functions.txt
+++ b/tests/functional/a/async_functions.txt
@@ -5,6 +5,6 @@ too-many-arguments:26:0:26:26:complex_function:Too many arguments (10/5):UNDEFIN
 too-many-branches:26:0:26:26:complex_function:Too many branches (13/12):UNDEFINED
 too-many-return-statements:26:0:26:26:complex_function:Too many return statements (10/6):UNDEFINED
 dangerous-default-value:59:0:59:14:func:Dangerous default value [] as argument:UNDEFINED
-duplicate-argument-name:59:18:59:19:func:Duplicate argument name a in function definition:HIGH
+duplicate-argument-name:59:18:59:19:func:Duplicate argument name 'a' in function definition:HIGH
 disallowed-name:64:0:64:13:foo:"Disallowed name ""foo""":HIGH
 empty-docstring:64:0:64:13:foo:Empty function docstring:HIGH
diff --git a/tests/functional/d/duplicate/duplicate_argument_name.py b/tests/functional/d/duplicate/duplicate_argument_name.py
index c0c68b43bb..a6654e6bb0 100644
--- a/tests/functional/d/duplicate/duplicate_argument_name.py
+++ b/tests/functional/d/duplicate/duplicate_argument_name.py
@@ -1,14 +1,28 @@
 """Check for duplicate function arguments."""
 
+# pylint: disable=missing-docstring, line-too-long, unused-argument
+
 
 def foo1(_, _): # [duplicate-argument-name]
-    """Function with duplicate argument name."""
+    ...
 
 def foo2(_abc, *, _abc): # [duplicate-argument-name]
-    """Function with duplicate argument name."""
+    ...
 
 def foo3(_, _=3): # [duplicate-argument-name]
-    """Function with duplicate argument name."""
+    ...
 
 def foo4(_, *, _): # [duplicate-argument-name]
-    """Function with duplicate argument name."""
+    ...
+
+def foo5(_, *_, _=3): # [duplicate-argument-name, duplicate-argument-name]
+    ...
+
+def foo6(a, *a): # [duplicate-argument-name]
+    ...
+
+def foo7(a, /, a): # [duplicate-argument-name]
+    ...
+
+def foo8(a, **a): # [duplicate-argument-name]
+    ...
diff --git a/tests/functional/d/duplicate/duplicate_argument_name.txt b/tests/functional/d/duplicate/duplicate_argument_name.txt
index 2925c5ac40..c565e88f40 100644
--- a/tests/functional/d/duplicate/duplicate_argument_name.txt
+++ b/tests/functional/d/duplicate/duplicate_argument_name.txt
@@ -1,4 +1,9 @@
-duplicate-argument-name:4:12:4:13:foo1:Duplicate argument name _ in function definition:HIGH
-duplicate-argument-name:7:18:7:22:foo2:Duplicate argument name _abc in function definition:HIGH
-duplicate-argument-name:10:12:10:13:foo3:Duplicate argument name _ in function definition:HIGH
-duplicate-argument-name:13:15:13:16:foo4:Duplicate argument name _ in function definition:HIGH
+duplicate-argument-name:6:12:6:13:foo1:Duplicate argument name '_' in function definition:HIGH
+duplicate-argument-name:9:18:9:22:foo2:Duplicate argument name '_abc' in function definition:HIGH
+duplicate-argument-name:12:12:12:13:foo3:Duplicate argument name '_' in function definition:HIGH
+duplicate-argument-name:15:15:15:16:foo4:Duplicate argument name '_' in function definition:HIGH
+duplicate-argument-name:18:13:18:14:foo5:Duplicate argument name '_' in function definition:HIGH
+duplicate-argument-name:18:16:18:17:foo5:Duplicate argument name '_' in function definition:HIGH
+duplicate-argument-name:21:13:21:14:foo6:Duplicate argument name 'a' in function definition:HIGH
+duplicate-argument-name:24:15:24:16:foo7:Duplicate argument name 'a' in function definition:HIGH
+duplicate-argument-name:27:14:27:15:foo8:Duplicate argument name 'a' in function definition:HIGH
diff --git a/tests/functional/d/duplicate/duplicate_argument_name_py3.py b/tests/functional/d/duplicate/duplicate_argument_name_py3.py
deleted file mode 100644
index 4751c6f2d3..0000000000
--- a/tests/functional/d/duplicate/duplicate_argument_name_py3.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Check for duplicate function keywordonly arguments."""
-
-
-def foo1(_, *_, _=3): # [duplicate-argument-name]
-    """Function with duplicate argument name."""
diff --git a/tests/functional/d/duplicate/duplicate_argument_name_py3.txt b/tests/functional/d/duplicate/duplicate_argument_name_py3.txt
deleted file mode 100644
index 3d6f6f8d9d..0000000000
--- a/tests/functional/d/duplicate/duplicate_argument_name_py3.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-duplicate-argument-name:4:16:4:17:foo1:Duplicate argument name _ in function definition:HIGH