Repository: pallets/click (python)

Issue:
Issue UserWarning when overriding Parameter name
When a command is given multiple parameters that use the same name, a UserWarning should be fired to highlight the conflict. Currently, the command will quietly allow one parameter to override the names of another.

(This is a slightly different problem than https://github.com/pallets/click/issues/1465. In that issue, True and False values were given the same names within a single parameter.)

**Example of the issue:**

```python
@click.command()
@click.option("-a", "--aardvark", is_flag=True)
@click.option("-a", "--avocado", is_flag=True)
def cli(aardvark: bool = False, avocado: bool = False):
    if aardvark:
        print("Animal")
    if avocado:
        print("Fruit")
```

In this scenario, the short-name, `-a`, appears ambiguous. A warning would help the user realize the conflict they've introduced.

I'll also provide a pull request for consideration.

Pull request description:
Give a UserWarning when Parameter is overriden by name
This adds a UserWarning when two parameters on one command have a name conflict. Very interested in thoughts and feedback.

- fixes #2396 

Checklist:

- [x] Add tests that demonstrate the correct behavior of the change. Tests should fail without the change.
- [x] Add or update relevant docs, in the docs folder and in code.
- [x] Add an entry in `CHANGES.rst` summarizing the change and linking to the issue.
- [x] Add `.. versionchanged::` entries in any relevant code docs.
- [x] Run `pre-commit` hooks and fix any issues.
- [x] Run `pytest` and `tox`, no tests failed.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index e603264..7bb0762 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -49,6 +49,8 @@ Unreleased
     ``Context.call_on_close`` callbacks and context managers added via
     ``Context.with_resource`` to be closed on exit as well. :pr:`2680`
 -   Add ``ProgressBar(hidden: bool)`` to allow hiding the progressbar. :issue:`2609`
+-   A ``UserWarning`` will be shown when multiple parameters attempt to use the
+    same name. :issue:`2396``
 
 
 Version 8.1.8
diff --git a/src/click/core.py b/src/click/core.py
index 968a10e..5420c4b 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -8,6 +8,7 @@ import os
 import sys
 import typing as t
 from collections import abc
+from collections import Counter
 from contextlib import AbstractContextManager
 from contextlib import contextmanager
 from contextlib import ExitStack
@@ -957,13 +958,29 @@ class Command:
         return formatter.getvalue().rstrip("\n")
 
     def get_params(self, ctx: Context) -> list[Parameter]:
-        rv = self.params
+        params = self.params
         help_option = self.get_help_option(ctx)
 
         if help_option is not None:
-            rv = [*rv, help_option]
+            params = [*params, help_option]
 
-        return rv
+        if __debug__:
+            import warnings
+
+            opts = [opt for param in params for opt in param.opts]
+            opts_counter = Counter(opts)
+            duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1)
+
+            for duplicate_opt in duplicate_opts:
+                warnings.warn(
+                    (
+                        f"The parameter {duplicate_opt} is used more than once. "
+                        "Remove its duplicate as parameters should be unique."
+                    ),
+                    stacklevel=3,
+                )
+
+        return params
 
     def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None:
         """Writes the usage line into the formatter.
@@ -1973,6 +1990,10 @@ class Parameter:
         of :class:`~click.shell_completion.CompletionItem` or a list of
         strings.
 
+    .. versionchanged:: 8.2
+        Adding duplicate parameter names to a :class:`~click.core.Command` will
+        result in a ``UserWarning`` being shown.
+
     .. versionchanged:: 8.0
         ``process_value`` validates required parameters and bounded
         ``nargs``, and invokes the parameter callback before returning
@@ -2974,7 +2995,7 @@ class Argument(Parameter):
         else:
             raise TypeError(
                 "Arguments take exactly one parameter declaration, got"
-                f" {len(decls)}."
+                f" {len(decls)}: {decls}."
             )
         return name, [arg], []
 


Test patch:
diff --git a/tests/test_arguments.py b/tests/test_arguments.py
index 7f6629f..825bcc4 100644
--- a/tests/test_arguments.py
+++ b/tests/test_arguments.py
@@ -401,3 +401,23 @@ def test_when_argument_decorator_is_used_multiple_times_cls_is_preserved():
 
     assert isinstance(foo.params[0], CustomArgument)
     assert isinstance(bar.params[0], CustomArgument)
+
+
+@pytest.mark.parametrize(
+    "args_one,args_two",
+    [
+        (
+            ("aardvark",),
+            ("aardvark",),
+        ),
+    ],
+)
+def test_duplicate_names_warning(runner, args_one, args_two):
+    @click.command()
+    @click.argument(*args_one)
+    @click.argument(*args_two)
+    def cli(one, two):
+        pass
+
+    with pytest.warns(UserWarning):
+        runner.invoke(cli, [])
diff --git a/tests/test_options.py b/tests/test_options.py
index 74ccc5f..2a3b692 100644
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -989,3 +989,29 @@ def test_usage_show_choices(runner, choices, metavars):
 
     result = runner.invoke(cli_without_choices, ["--help"])
     assert metavars in result.output
+
+
+@pytest.mark.parametrize(
+    "opts_one,opts_two",
+    [
+        # No duplicate shortnames
+        (
+            ("-a", "--aardvark"),
+            ("-a", "--avocado"),
+        ),
+        # No duplicate long names
+        (
+            ("-a", "--aardvark"),
+            ("-b", "--aardvark"),
+        ),
+    ],
+)
+def test_duplicate_names_warning(runner, opts_one, opts_two):
+    @click.command()
+    @click.option(*opts_one)
+    @click.option(*opts_two)
+    def cli(one, two):
+        pass
+
+    with pytest.warns(UserWarning):
+        runner.invoke(cli, [])