diff --git a/src/click/parser.py b/src/click/parser.py index 1ea1f7166e..af3d2ef6a5 100644 --- a/src/click/parser.py +++ b/src/click/parser.py @@ -401,6 +401,18 @@ def _match_short_opt(self, arg: str, state: _ParsingState) -> None: if self.ignore_unknown_options: unknown_options.append(ch) continue + + # If a registered single-dash long option (like -dbg) + # is a prefix of the original argument, report the + # error using the full argument instead of just the + # single character that failed to match. + norm_arg = _normalize_opt(arg, self.ctx) + for long_opt_name in self._long_opt: + if norm_arg.startswith(long_opt_name) and len( + long_opt_name + ) > len(opt): + raise NoSuchOption(norm_arg, ctx=self.ctx) + raise NoSuchOption(opt, ctx=self.ctx) if option.takes_value: # Any characters left in arg? Pretend they're the diff --git a/tests/test_options.py b/tests/test_options.py index e335f3c1a0..d1c845c84e 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -144,6 +144,26 @@ def cli(): assert f"No such option: {unknown_flag}" in result.output +def test_multicharacter_short_option_wrong_option_error(runner): + """When a wrong multi-character short option like -dbgwrong is passed, + the error should reference the full argument, not just the first char.""" + + @click.command() + @click.option("-dbg", is_flag=True) + def cli(dbg): + click.echo(f"dbg={dbg}") + + # Correct usage should work. + result = runner.invoke(cli, ["-dbg"]) + assert not result.exception + assert "dbg=True" in result.output + + # Wrong option should mention the full argument, not just '-d'. + result = runner.invoke(cli, ["-dbgwrong"]) + assert result.exit_code != 0 + assert "No such option: -dbgwrong" in result.output + + @pytest.mark.parametrize( ("value", "expect"), [