From 2c8282128e64a96c9873507e7c32c3bc02335ff8 Mon Sep 17 00:00:00 2001 From: Rayan-and-beyond <263488867+Rayan-and-beyond@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:21:34 +0000 Subject: [PATCH] fix: parse explicit short flag values --- README.md | 6 +++++- flag.go | 12 ++++++------ flag_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0c8b00d4..9f01ea57 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,11 @@ flag.VarP(&flagVal, "varname", "v", "help message") ``` Shorthand letters can be used with single dashes on the command line. -Boolean shorthand flags can be combined with other shorthand flags. +Boolean shorthand flags can be combined with other shorthand flags. A shorthand +with `NoOptDefVal` behaves the same way inside a cluster: for example, if `-a` +has a no-option default, `-abc` uses that default for `-a` and continues with +`-b` and `-c`. To pass an explicit value to such a shorthand, use `-a=value` +or `-a value`. The default set of command-line flags is controlled by top-level functions. The FlagSet type allows one to define diff --git a/flag.go b/flag.go index a4affd48..16acc621 100644 --- a/flag.go +++ b/flag.go @@ -1184,17 +1184,17 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse // '-f=arg' value = shorthands[2:] outShorts = "" - } else if flag.NoOptDefVal != "" { - // '-f' (arg was optional) - value = flag.NoOptDefVal - } else if len(shorthands) > 1 { + } else if len(shorthands) > 1 && flag.NoOptDefVal == "" { // '-farg' value = shorthands[1:] outShorts = "" - } else if len(args) > 0 { - // '-f arg' + } else if len(shorthands) == 1 && len(args) > 0 && !strings.HasPrefix(args[0], "-") && !isNoOptBoolValue(flag.Value) { + // '-f arg' for non-boolean optional-value flags value = args[0] outArgs = args[1:] + } else if flag.NoOptDefVal != "" { + // '-f' (arg was optional), including '-fb' shorthand clusters + value = flag.NoOptDefVal } else { // '-f' (arg was required) err = f.fail(&ValueRequiredError{ diff --git a/flag_test.go b/flag_test.go index 1e26d190..75b174f9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1626,3 +1626,33 @@ func TestVisitFlagOrder(t *testing.T) { i++ }) } + +func TestShortFlagNoOptDefValWithExplicitValue(t *testing.T) { + f := NewFlagSet("test", ContinueOnError) + value := f.StringP("value", "v", "initial", "value") + f.Lookup("value").NoOptDefVal = "default" + + if err := f.Parse([]string{"-v", "explicit"}); err != nil { + t.Fatal(err) + } + if got, want := *value, "explicit"; got != want { + t.Fatalf("-v explicit = %q, want %q", got, want) + } +} + +func TestShortFlagNoOptDefValInClusterKeepsCurrentSemantics(t *testing.T) { + f := NewFlagSet("test", ContinueOnError) + value := f.StringP("value", "v", "initial", "value") + f.Lookup("value").NoOptDefVal = "default" + b := f.BoolP("bool", "b", false, "bool") + + if err := f.Parse([]string{"-vb"}); err != nil { + t.Fatal(err) + } + if got, want := *value, "default"; got != want { + t.Fatalf("-vb value = %q, want %q", got, want) + } + if !*b { + t.Fatal("-vb did not parse -b after -v used its NoOptDefVal") + } +}