parseopt: check for duplicate long names and numerical options
We already check for duplicate short names. Check for and report duplicate long names and numerical options as well. Perform the slightly expensive string duplicate check only when showing the usage to keep the cost of normal invocations low. t0012-help.sh covers it. Helped-by: Jeff King <peff@peff.net> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committed
Feb 28, 2026 at 10:19 UTC
237e520d81201eee609cf21e24f1c7ac6719ec8a
1 file changed
+23
parse-options.c
+23
@@ -5,6 +5,7 @@
5
#include "gettext.h"
6
#include "strbuf.h"
7
#include "string-list.h"
8
+#include "strmap.h"
9
#include "utf8.h"
10
11
static int disallow_abbreviated_options;
@@ -634,6 +635,7 @@ static void check_typos(const char *arg, const struct option *options)
635
static void parse_options_check(const struct option *opts)
636
{
637
char short_opts[128];
638
+ bool saw_number_option = false;
639
void *subcommand_value = NULL;
640
641
memset(short_opts, '\0', sizeof(short_opts));
@@ -648,6 +650,11 @@ static void parse_options_check(const struct option *opts)
650
else if (short_opts[opts->short_name]++)
651
optbug(opts, "short name already used");
652
}
653
+ if (opts->type == OPTION_NUMBER) {
654
+ if (saw_number_option)
655
+ optbug(opts, "duplicate numerical option");
656
+ saw_number_option = true;
657
+ }
658
if (opts->flags & PARSE_OPT_NODASH &&
659
((opts->flags & PARSE_OPT_OPTARG) ||
660
!(opts->flags & PARSE_OPT_NOARG) ||
@@ -707,6 +714,20 @@ static void parse_options_check(const struct option *opts)
714
BUG_if_bug("invalid 'struct option'");
715
}
716
717
+static void parse_options_check_harder(const struct option *opts)
718
+{
719
+ struct strset long_names = STRSET_INIT;
720
+
721
+ for (; opts->type != OPTION_END; opts++) {
722
+ if (opts->long_name) {
723
+ if (!strset_add(&long_names, opts->long_name))
724
+ optbug(opts, "long name already used");
725
+ }
726
+ }
727
+ BUG_if_bug("invalid 'struct option'");
728
+ strset_clear(&long_names);
729
+}
730
+
731
static int has_subcommands(const struct option *options)
732
{
733
for (; options->type != OPTION_END; options++)
@@ -1324,6 +1345,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t
1345
const char *prefix = usage_prefix;
1346
int saw_empty_line = 0;
1347
1348
+ parse_options_check_harder(opts);
1349
+
1350
if (!usagestr)
1351
return PARSE_OPT_HELP;
1352