Repository: pallets/click

Problem statement:
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.


Hints:

Patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -8,6 +8,7 @@
 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 @@ def get_usage(self, ctx: Context) -> str:
         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 @@ def _parse_decls(
         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
--- a/tests/test_arguments.py
+++ b/tests/test_arguments.py
@@ -401,3 +401,23 @@ def bar(arg):
 
     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
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -989,3 +989,29 @@ def cli_without_choices(g):
 
     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, [])