From a2f8cc156fa9ca511d3af19c9e8de88ff9adf4b4 Mon Sep 17 00:00:00 2001 From: Haim Dimer Date: Thu, 30 Jul 2026 10:32:59 -0700 Subject: [PATCH] fix: report the flag name as typed in deprecation warnings When a NormalizeFunc aliases a flag name, using the alias for a deprecated flag printed the canonical name rather than the one the user typed. Parse passed the normalized flag.Name into Set, so the typed name was lost before the warning was built. Thread the typed name through the internal parseFunc so the warning reports it. Long flags report the typed alias; shorthands keep the canonical long name. Public Set/ParseAll signatures are unchanged. Fixes #279 --- flag.go | 22 +++++++++++++++------- flag_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/flag.go b/flag.go index ced8440a..fd49a788 100644 --- a/flag.go +++ b/flag.go @@ -567,7 +567,7 @@ func (f *FlagSet) Set(name, value string) error { } if flag.Deprecated != "" { - _, _ = fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + _, _ = fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", name, flag.Deprecated) } return nil } @@ -1110,7 +1110,9 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin return } - err = fn(flag, value) + // Pass the name the user actually typed so a deprecation message reports + // it rather than the canonical name a NormalizeFunc may have mapped it to. + err = fn(flag, name, value) if err != nil { err = f.fail(err) } @@ -1200,7 +1202,9 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse _, _ = fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) } - err = fn(flag, value) + // A shorthand has no alias to report, so a deprecation message keeps the + // canonical long name. + err = fn(flag, flag.Name, value) if err != nil { err = f.fail(err) } @@ -1294,8 +1298,8 @@ func (f *FlagSet) Parse(arguments []string) error { return nil } - set := func(flag *Flag, value string) error { - return f.Set(flag.Name, value) + set := func(flag *Flag, name, value string) error { + return f.Set(name, value) } err := f.parseArgs(arguments, set) @@ -1316,7 +1320,7 @@ func (f *FlagSet) Parse(arguments []string) error { return nil } -type parseFunc func(flag *Flag, value string) error +type parseFunc func(flag *Flag, name, value string) error // ParseAll parses flag definitions from the argument list, which should not // include the command name. The arguments for fn are flag and value. Must be @@ -1327,7 +1331,11 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) f.parsed = true f.args = make([]string, 0, len(arguments)) - err := f.parseArgs(arguments, fn) + set := func(flag *Flag, name, value string) error { + return fn(flag, value) + } + + err := f.parseArgs(arguments, set) if err != nil { switch f.errorHandling { case ContinueOnError: diff --git a/flag_test.go b/flag_test.go index 82fcd48a..509a8c70 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1401,6 +1401,33 @@ func TestDeprecatedFlagUsageNormalized(t *testing.T) { } } +func TestDeprecatedFlagUsageAlias(t *testing.T) { + // When a NormalizeFunc aliases one flag name to another, using the alias + // to set a deprecated flag should report the name the user actually typed, + // not the canonical name. See https://github.com/spf13/pflag/issues/279. + f := NewFlagSet("bob", ContinueOnError) + f.String("src", "", "src directory or file") + f.SetNormalizeFunc(func(_ *FlagSet, name string) NormalizedName { + if name == "dir" { + name = "src" + } + return NormalizedName(name) + }) + _ = f.MarkDeprecated("src", "use --src") + + out, err := parseReturnStderr(t, f, []string{"--dir=x"}) + if err != nil { + t.Fatal("expected no error; got ", err) + } + + if !strings.Contains(out, "Flag --dir has been deprecated") { + t.Errorf("expected the deprecation message to name the flag the user typed (--dir); got: %q", out) + } + if strings.Contains(out, "Flag --src has been deprecated") { + t.Errorf("deprecation message should not name the canonical flag (--src); got: %q", out) + } +} + // Name normalization function should be called only once on flag addition func TestMultipleNormalizeFlagNameInvocations(t *testing.T) { normalizeFlagNameInvocations = 0