From 6f1cdf1e28c5b285f6624e32fcfb7f26d09b2848 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 | 20 +++++++++++++------- flag_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/flag.go b/flag.go index a4affd48..2670bb79 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 } @@ -1119,7 +1119,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) } @@ -1209,7 +1211,7 @@ 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) + err = fn(flag, flag.Name, value) if err != nil { err = f.fail(err) } @@ -1303,8 +1305,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, name, value string) error { + return f.Set(name, value) } err := f.parseArgs(arguments, set) @@ -1325,7 +1327,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 @@ -1336,7 +1338,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, _, 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 1e26d190..6d23feb6 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1413,6 +1413,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 --dest") + + 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