From 23645f3ad694bee73c73818d0c89d44b758c98a8 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 20 Mar 2026 11:58:54 -0700 Subject: [PATCH 1/4] Use slices.Contains This replaces a few loops with a call to slices.Contains. The slices package was added in Go 1.21. Signed-off-by: Kir Kolyshkin --- flag.go | 9 ++------- help.go | 19 ++++++++----------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/flag.go b/flag.go index bfac8faaf6..1a65948ec4 100644 --- a/flag.go +++ b/flag.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "slices" "strings" "time" ) @@ -208,13 +209,7 @@ func FlagNames(name string, aliases []string) []string { } func hasFlag(flags []Flag, fl Flag) bool { - for _, existing := range flags { - if fl == existing { - return true - } - } - - return false + return slices.Contains(flags, fl) } func flagSplitMultiValues(val string, sliceSeparator string, disableSliceSeparator bool) []string { diff --git a/help.go b/help.go index 9db9df510c..0f7f629caf 100644 --- a/help.go +++ b/help.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "slices" "strings" "text/tabwriter" "text/template" @@ -204,10 +205,8 @@ func cliArgContains(flagName string, args []string) bool { count = 2 } flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - for _, a := range args { - if a == flag { - return true - } + if slices.Contains(args, flag) { + return true } } return false @@ -496,13 +495,11 @@ func checkShellCompleteFlag(c *Command, arguments []string) (bool, []string) { return false, arguments } - for _, arg := range arguments { - // If arguments include "--", shell completion is disabled - // because after "--" only positional arguments are accepted. - // https://unix.stackexchange.com/a/11382 - if arg == "--" { - return false, arguments[:pos] - } + // If arguments include "--", shell completion is disabled + // because after "--" only positional arguments are accepted. + // https://unix.stackexchange.com/a/11382 + if slices.Contains(arguments, "--") { + return false, arguments[:pos] } return true, arguments[:pos] From 1d45fd31b796fa9dadb5a1722c219ab204aca03f Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 20 Mar 2026 12:01:48 -0700 Subject: [PATCH 2/4] Replace interface{} with any The any alias was introduced in Go 1.18. This is a purely stylistic change that makes code more readable. Changes in .go files generated by modernize -any -fix ./... PS godoc-current.txt and testdata/godoc-v3.x.txt modified accordingly. Signed-off-by: Kir Kolyshkin --- command.go | 4 ++-- command_test.go | 2 +- completion_test.go | 16 ++++++++-------- flag_bool.go | 2 +- flag_map_impl.go | 2 +- flag_slice_base.go | 2 +- flag_test.go | 2 +- godoc-current.txt | 8 ++++---- help_test.go | 36 ++++++++++++++++++------------------ testdata/godoc-v3.x.txt | 8 ++++---- 10 files changed, 41 insertions(+), 41 deletions(-) diff --git a/command.go b/command.go index 8f73fd28d7..cfdc5601dd 100644 --- a/command.go +++ b/command.go @@ -90,7 +90,7 @@ type Command struct { // default behavior. ExitErrHandler ExitErrHandlerFunc `json:"-"` // Other custom info - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` // Carries a function which returns app specific info. ExtraInfo func() map[string]string `json:"-"` // CustomRootCommandHelpTemplate the text template for app help topic. @@ -559,7 +559,7 @@ func (cmd *Command) Count(name string) int { } // Value returns the value of the flag corresponding to `name` -func (cmd *Command) Value(name string) interface{} { +func (cmd *Command) Value(name string) any { if fs := cmd.lookupFlag(name); fs != nil { tracef("value found for name %[1]q (cmd=%[2]q)", name, cmd.Name) return fs.Get() diff --git a/command_test.go b/command_test.go index b3cfbc4788..28713f6647 100644 --- a/command_test.go +++ b/command_test.go @@ -1973,7 +1973,7 @@ func TestCommandHelpPrinter(t *testing.T) { }() wasCalled := false - HelpPrinter = func(io.Writer, string, interface{}) { + HelpPrinter = func(io.Writer, string, any) { wasCalled = true } diff --git a/completion_test.go b/completion_test.go index 17112d02fa..4f921c3f75 100644 --- a/completion_test.go +++ b/completion_test.go @@ -69,7 +69,7 @@ func TestCompletionSubcommand(t *testing.T) { args []string contains string msg string - msgArgs []interface{} + msgArgs []any notContains bool }{ { @@ -77,7 +77,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", completionFlag}, contains: "xyz", msg: "Expected output to contain shell name %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "xyz", }, }, @@ -86,7 +86,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "-", completionFlag}, contains: "l1", msg: "Expected output to contain shell name %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "l1", }, }, @@ -95,7 +95,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "--", completionFlag}, contains: "l1", msg: "Expected output to contain shell name %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "l1", }, notContains: true, @@ -105,7 +105,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "xyz", completionFlag}, contains: "-g", msg: "Expected output to contain flag %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "-g", }, notContains: true, @@ -115,7 +115,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "xyz", "-", completionFlag}, contains: "-g", msg: "Expected output to contain flag %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "-g", }, }, @@ -124,7 +124,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "xyz", "--", completionFlag}, contains: "-g", msg: "Expected output to contain flag %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "-g", }, notContains: true, @@ -134,7 +134,7 @@ func TestCompletionSubcommand(t *testing.T) { args: []string{"foo", "bar", "xyz", "--", "sargs", completionFlag}, contains: "-g", msg: "Expected output to contain flag %[1]q", - msgArgs: []interface{}{ + msgArgs: []any{ "-g", }, notContains: true, diff --git a/flag_bool.go b/flag_bool.go index 0f2af27bf2..f0ec22fafa 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -69,7 +69,7 @@ func (b *boolValue) Set(s string) error { return err } -func (b *boolValue) Get() interface{} { return *b.destination } +func (b *boolValue) Get() any { return *b.destination } func (b *boolValue) String() string { return strconv.FormatBool(*b.destination) diff --git a/flag_map_impl.go b/flag_map_impl.go index b56d0a9733..cb65903b92 100644 --- a/flag_map_impl.go +++ b/flag_map_impl.go @@ -115,7 +115,7 @@ func (i *MapBase[T, C, VC]) Value() map[string]T { } // Get returns the mapping of values set by this flag -func (i *MapBase[T, C, VC]) Get() interface{} { +func (i *MapBase[T, C, VC]) Get() any { return *i.dict } diff --git a/flag_slice_base.go b/flag_slice_base.go index 0248d8f1dc..1f9561ae21 100644 --- a/flag_slice_base.go +++ b/flag_slice_base.go @@ -105,7 +105,7 @@ func (i *SliceBase[T, C, VC]) Value() []T { } // Get returns the slice of values set by this flag -func (i *SliceBase[T, C, VC]) Get() interface{} { +func (i *SliceBase[T, C, VC]) Get() any { return *i.slice } diff --git a/flag_test.go b/flag_test.go index 712e9c96a9..7d7a80b359 100644 --- a/flag_test.go +++ b/flag_test.go @@ -36,7 +36,7 @@ func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } -func (p *Parser) Get() interface{} { +func (p *Parser) Get() any { return p } diff --git a/godoc-current.txt b/godoc-current.txt index 9784c4e2b3..a31fb21089 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -499,7 +499,7 @@ type Command struct { // default behavior. ExitErrHandler ExitErrHandlerFunc `json:"-"` // Other custom info - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` // Carries a function which returns app specific info. ExtraInfo func() map[string]string `json:"-"` // CustomRootCommandHelpTemplate the text template for app help topic. @@ -777,7 +777,7 @@ func (cmd *Command) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found -func (cmd *Command) Value(name string) interface{} +func (cmd *Command) Value(name string) any Value returns the value of the flag corresponding to `name` func (cmd *Command) VisibleCategories() []CommandCategory @@ -1225,7 +1225,7 @@ func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *Map func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value -func (i *MapBase[T, C, VC]) Get() interface{} +func (i *MapBase[T, C, VC]) Get() any Get returns the mapping of values set by this flag func (i *MapBase[T, C, VC]) Serialize() string @@ -1312,7 +1312,7 @@ func NewSliceBase[T any, C any, VC ValueCreator[T, C]](defaults ...T) *SliceBase func (i SliceBase[T, C, VC]) Create(val []T, p *[]T, c C) Value -func (i *SliceBase[T, C, VC]) Get() interface{} +func (i *SliceBase[T, C, VC]) Get() any Get returns the slice of values set by this flag func (i *SliceBase[T, C, VC]) Serialize() string diff --git a/help_test.go b/help_test.go index 6a11bd9e19..e5c72cad68 100644 --- a/help_test.go +++ b/help_test.go @@ -481,7 +481,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) { { name: "no-command", template: "", - printer: func(w io.Writer, _ string, _ interface{}) { + printer: func(w io.Writer, _ string, _ any) { fmt.Fprint(w, "yo") }, command: "", @@ -517,7 +517,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) { defer func(old HelpPrinterFunc) { HelpPrinter = old }(HelpPrinter) - HelpPrinter = func(w io.Writer, templ string, data interface{}) { + HelpPrinter = func(w io.Writer, templ string, data any) { assert.Equal(t, tt.wantTemplate, templ, "template mismatch") tt.printer(w, templ, data) } @@ -947,7 +947,7 @@ func TestShowRootCommandHelp_HelpPrinter(t *testing.T) { { name: "standard-command", template: "", - printer: func(w io.Writer, _ string, _ interface{}) { + printer: func(w io.Writer, _ string, _ any) { fmt.Fprint(w, "yo") }, wantTemplate: RootCommandHelpTemplate, @@ -956,7 +956,7 @@ func TestShowRootCommandHelp_HelpPrinter(t *testing.T) { { name: "custom-template-command", template: "{{doublecho .Name}}", - printer: func(w io.Writer, templ string, data interface{}) { + printer: func(w io.Writer, templ string, data any) { // Pass a custom function to ensure it gets used fm := map[string]any{"doublecho": doublecho} DefaultPrintHelpCustom(w, templ, data, fm) @@ -971,7 +971,7 @@ func TestShowRootCommandHelp_HelpPrinter(t *testing.T) { defer func(old HelpPrinterFunc) { HelpPrinter = old }(HelpPrinter) - HelpPrinter = func(w io.Writer, templ string, data interface{}) { + HelpPrinter = func(w io.Writer, templ string, data any) { assert.Equal(t, tt.wantTemplate, templ, "unexpected template") tt.printer(w, templ, data) } @@ -1006,7 +1006,7 @@ func TestShowRootCommandHelp_HelpPrinterCustom(t *testing.T) { { name: "standard-command", template: "", - printer: func(w io.Writer, _ string, _ interface{}, _ map[string]interface{}) { + printer: func(w io.Writer, _ string, _ any, _ map[string]any) { fmt.Fprint(w, "yo") }, wantTemplate: RootCommandHelpTemplate, @@ -1015,7 +1015,7 @@ func TestShowRootCommandHelp_HelpPrinterCustom(t *testing.T) { { name: "custom-template-command", template: "{{doublecho .Name}}", - printer: func(w io.Writer, templ string, data interface{}, _ map[string]interface{}) { + printer: func(w io.Writer, templ string, data any, _ map[string]any) { // Pass a custom function to ensure it gets used fm := map[string]any{"doublecho": doublecho} DefaultPrintHelpCustom(w, templ, data, fm) @@ -1030,7 +1030,7 @@ func TestShowRootCommandHelp_HelpPrinterCustom(t *testing.T) { defer func(old HelpPrinterCustomFunc) { HelpPrinterCustom = old }(HelpPrinterCustom) - HelpPrinterCustom = func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { + HelpPrinterCustom = func(w io.Writer, templ string, data any, fm map[string]any) { assert.Nil(t, fm, "unexpected function map passed") assert.Equal(t, tt.wantTemplate, templ, "unexpected template") tt.printer(w, templ, data, fm) @@ -1514,8 +1514,8 @@ Including newlines. And then another long line. Blah blah blah does anybody ever read these things?`, } - HelpPrinter = func(w io.Writer, templ string, data interface{}) { - funcMap := map[string]interface{}{ + HelpPrinter = func(w io.Writer, templ string, data any) { + funcMap := map[string]any{ "wrapAt": func() int { return 30 }, @@ -1597,8 +1597,8 @@ func TestWrappedCommandHelp(t *testing.T) { cmd.setupDefaults([]string{"cli.test"}) cmd.setupCommandGraph() - HelpPrinter = func(w io.Writer, templ string, data interface{}) { - funcMap := map[string]interface{}{ + HelpPrinter = func(w io.Writer, templ string, data any) { + funcMap := map[string]any{ "wrapAt": func() int { return 30 }, @@ -1665,8 +1665,8 @@ func TestWrappedSubcommandHelp(t *testing.T) { }, } - HelpPrinter = func(w io.Writer, templ string, data interface{}) { - funcMap := map[string]interface{}{ + HelpPrinter = func(w io.Writer, templ string, data any) { + funcMap := map[string]any{ "wrapAt": func() int { return 30 }, @@ -1737,8 +1737,8 @@ func TestWrappedHelpSubcommand(t *testing.T) { }, } - HelpPrinter = func(w io.Writer, templ string, data interface{}) { - funcMap := map[string]interface{}{ + HelpPrinter = func(w io.Writer, templ string, data any) { + funcMap := map[string]any{ "wrapAt": func() int { return 30 }, @@ -1821,8 +1821,8 @@ func TestCategorizedHelp(t *testing.T) { }, } - HelpPrinter = func(w io.Writer, templ string, data interface{}) { - funcMap := map[string]interface{}{ + HelpPrinter = func(w io.Writer, templ string, data any) { + funcMap := map[string]any{ "wrapAt": func() int { return 30 }, diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 9784c4e2b3..a31fb21089 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -499,7 +499,7 @@ type Command struct { // default behavior. ExitErrHandler ExitErrHandlerFunc `json:"-"` // Other custom info - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` // Carries a function which returns app specific info. ExtraInfo func() map[string]string `json:"-"` // CustomRootCommandHelpTemplate the text template for app help topic. @@ -777,7 +777,7 @@ func (cmd *Command) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found -func (cmd *Command) Value(name string) interface{} +func (cmd *Command) Value(name string) any Value returns the value of the flag corresponding to `name` func (cmd *Command) VisibleCategories() []CommandCategory @@ -1225,7 +1225,7 @@ func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *Map func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value -func (i *MapBase[T, C, VC]) Get() interface{} +func (i *MapBase[T, C, VC]) Get() any Get returns the mapping of values set by this flag func (i *MapBase[T, C, VC]) Serialize() string @@ -1312,7 +1312,7 @@ func NewSliceBase[T any, C any, VC ValueCreator[T, C]](defaults ...T) *SliceBase func (i SliceBase[T, C, VC]) Create(val []T, p *[]T, c C) Value -func (i *SliceBase[T, C, VC]) Get() interface{} +func (i *SliceBase[T, C, VC]) Get() any Get returns the slice of values set by this flag func (i *SliceBase[T, C, VC]) Serialize() string From 0f6449894ea70beaaa72827dae09cf90b73aa825 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 20 Mar 2026 13:20:10 -0700 Subject: [PATCH 3/4] Use for range for integers This uses more idiomatic Go 1.22 style. Generated by: modernize -rangeint -fix ./... Signed-off-by: Kir Kolyshkin --- command_test.go | 2 +- suggestions.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command_test.go b/command_test.go index 28713f6647..88369cc0fa 100644 --- a/command_test.go +++ b/command_test.go @@ -5420,7 +5420,7 @@ func TestCommand_ExclusiveFlagsWithAfter(t *testing.T) { func TestCommand_ParallelRun(t *testing.T) { t.Parallel() - for i := 0; i < 10; i++ { + for i := range 10 { t.Run(fmt.Sprintf("run_%d", i), func(t *testing.T) { t.Parallel() diff --git a/suggestions.go b/suggestions.go index 6f29f12213..401dcef253 100644 --- a/suggestions.go +++ b/suggestions.go @@ -92,7 +92,7 @@ func jaroWinkler(a, b string) float64 { prefix := int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b))))) var prefixMatch float64 - for i := 0; i < prefix; i++ { + for i := range prefix { if a[i] == b[i] { prefixMatch++ } else { From 0250ac6fa0ebc1fdd5f2f48d4f0af1eb422b46fe Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 20 Mar 2026 13:22:56 -0700 Subject: [PATCH 4/4] Use strings.Builder This function is available since Go 1.10 and improves performance by reducing strings allocations. Signed-off-by: Kir Kolyshkin --- docs.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs.go b/docs.go index 8ecdfc8359..88461386b8 100644 --- a/docs.go +++ b/docs.go @@ -35,21 +35,21 @@ func unquoteUsage(usage string) (string, string) { } func prefixedNames(names []string, placeholder string) string { - var prefixed string + var prefixed strings.Builder for i, name := range names { if name == "" { continue } - prefixed += prefixFor(name) + name + prefixed.WriteString(prefixFor(name) + name) if placeholder != "" { - prefixed += " " + placeholder + prefixed.WriteString(" " + placeholder) } if i < len(names)-1 { - prefixed += ", " + prefixed.WriteString(", ") } } - return prefixed + return prefixed.String() } func envFormat(envVars []string, prefix, sep, suffix string) string {