From fe6f62b9b2badad15cc66b15d34d3652f63867bc Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Thu, 29 Sep 2022 10:33:48 -0400 Subject: [PATCH 1/3] Fix:(issue_1500). Fix slice flag value duplication issue --- app_test.go | 31 +++++++++++++++++++++++++++++++ godoc-current.txt | 42 ++++++++++++++++++++++++++++++++++-------- parse.go | 5 ++++- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/app_test.go b/app_test.go index f7ecd13..a4ce72c 100644 --- a/app_test.go +++ b/app_test.go @@ -956,6 +956,37 @@ func TestApp_UseShortOptionHandlingSubCommand_missing_value(t *testing.T) { expect(t, err, errors.New("flag needs an argument: -n")) } +func TestApp_UseShortOptionAfterSliceFlag(t *testing.T) { + var one, two bool + var name string + var sliceValDest StringSlice + var sliceVal []string + expected := "expectedName" + + app := newTestApp() + app.UseShortOptionHandling = true + app.Flags = []Flag{ + &StringSliceFlag{Name: "env", Aliases: []string{"e"}, Destination: &sliceValDest}, + &BoolFlag{Name: "one", Aliases: []string{"o"}}, + &BoolFlag{Name: "two", Aliases: []string{"t"}}, + &StringFlag{Name: "name", Aliases: []string{"n"}}, + } + app.Action = func(c *Context) error { + sliceVal = c.StringSlice("env") + one = c.Bool("one") + two = c.Bool("two") + name = c.String("name") + return nil + } + + _ = app.Run([]string{"", "-e", "foo", "-on", expected}) + expect(t, sliceVal, []string{"foo"}) + expect(t, sliceValDest.Value(), []string{"foo"}) + expect(t, one, true) + expect(t, two, false) + expect(t, name, expected) +} + func TestApp_Float64Flag(t *testing.T) { var meters float64 diff --git a/godoc-current.txt b/godoc-current.txt index 3d5cb9a..efbac4d 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -458,6 +458,8 @@ type BoolFlag struct { EnvVars []string Count *int + + Action func(*Context, bool) error } BoolFlag is a flag with type bool @@ -565,7 +567,6 @@ type Command struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string - // Has unexported fields. } Command is a subcommand for a cli.App. @@ -584,13 +585,6 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c *Command) VisibleCategories() []CommandCategory - VisibleCategories returns a slice of categories and commands that are - Hidden=false - -func (c *Command) VisibleCommands() []*Command - VisibleCommands returns a slice of the Commands with Hidden=false - func (c *Command) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain @@ -776,6 +770,8 @@ type DurationFlag struct { Aliases []string EnvVars []string + + Action func(*Context, time.Duration) error } DurationFlag is a flag with type time.Duration @@ -952,6 +948,8 @@ type Float64Flag struct { Aliases []string EnvVars []string + + Action func(*Context, float64) error } Float64Flag is a flag with type float64 @@ -1038,6 +1036,8 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []float64) error } Float64SliceFlag is a flag with type *Float64Slice @@ -1115,6 +1115,8 @@ type GenericFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, interface{}) error } GenericFlag is a flag with type Generic @@ -1181,6 +1183,8 @@ type Int64Flag struct { EnvVars []string Base int + + Action func(*Context, int64) error } Int64Flag is a flag with type int64 @@ -1267,6 +1271,8 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int64) error } Int64SliceFlag is a flag with type *Int64Slice @@ -1338,6 +1344,8 @@ type IntFlag struct { EnvVars []string Base int + + Action func(*Context, int) error } IntFlag is a flag with type int @@ -1428,6 +1436,8 @@ type IntSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int) error } IntSliceFlag is a flag with type *IntSlice @@ -1533,6 +1543,8 @@ type PathFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, Path) error } PathFlag is a flag with type Path @@ -1673,6 +1685,8 @@ type StringFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, string) error } StringFlag is a flag with type string @@ -1761,6 +1775,8 @@ type StringSliceFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, []string) error } StringSliceFlag is a flag with type *StringSlice @@ -1867,6 +1883,8 @@ type TimestampFlag struct { Layout string Timezone *time.Location + + Action func(*Context, *time.Time) error } TimestampFlag is a flag with type *Timestamp @@ -1932,6 +1950,8 @@ type Uint64Flag struct { EnvVars []string Base int + + Action func(*Context, uint64) error } Uint64Flag is a flag with type uint64 @@ -2018,6 +2038,8 @@ type Uint64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint64) error } Uint64SliceFlag is a flag with type *Uint64Slice @@ -2080,6 +2102,8 @@ type UintFlag struct { EnvVars []string Base int + + Action func(*Context, uint) error } UintFlag is a flag with type uint @@ -2170,6 +2194,8 @@ type UintSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint) error } UintSliceFlag is a flag with type *UintSlice diff --git a/parse.go b/parse.go index a2db306..28e24f1 100644 --- a/parse.go +++ b/parse.go @@ -46,7 +46,10 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple } // swap current argument with the split version - args = append(args[:i], append(shortOpts, args[i+1:]...)...) + // do not include args that parsed correctly so far as it would + // trigger Value.Set() on those args and would result in + // duplicates for slice type flags + args = append(shortOpts, args[i+1:]...) argsWereSplit = true break } From 2da03245be8173b5274feb673663dba99ea2f35c Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Wed, 5 Oct 2022 09:33:41 -0400 Subject: [PATCH 2/3] Fix failed test --- parse.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/parse.go b/parse.go index 28e24f1..d79f15a 100644 --- a/parse.go +++ b/parse.go @@ -59,13 +59,6 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple if !argsWereSplit { return err } - - // Since custom parsing failed, replace the flag set before retrying - newSet, err := ip.newFlagSet() - if err != nil { - return err - } - *set = *newSet } } From a27294dcd02b042f58e3e912fc0377fa16d69519 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Wed, 5 Oct 2022 09:37:31 -0400 Subject: [PATCH 3/3] Run make v2approve --- testdata/godoc-v2.x.txt | 42 +++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 3d5cb9a..efbac4d 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -458,6 +458,8 @@ type BoolFlag struct { EnvVars []string Count *int + + Action func(*Context, bool) error } BoolFlag is a flag with type bool @@ -565,7 +567,6 @@ type Command struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string - // Has unexported fields. } Command is a subcommand for a cli.App. @@ -584,13 +585,6 @@ func (c *Command) Run(ctx *Context) (err error) Run invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c *Command) VisibleCategories() []CommandCategory - VisibleCategories returns a slice of categories and commands that are - Hidden=false - -func (c *Command) VisibleCommands() []*Command - VisibleCommands returns a slice of the Commands with Hidden=false - func (c *Command) VisibleFlagCategories() []VisibleFlagCategory VisibleFlagCategories returns a slice containing all the visible flag categories with the flags they contain @@ -776,6 +770,8 @@ type DurationFlag struct { Aliases []string EnvVars []string + + Action func(*Context, time.Duration) error } DurationFlag is a flag with type time.Duration @@ -952,6 +948,8 @@ type Float64Flag struct { Aliases []string EnvVars []string + + Action func(*Context, float64) error } Float64Flag is a flag with type float64 @@ -1038,6 +1036,8 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []float64) error } Float64SliceFlag is a flag with type *Float64Slice @@ -1115,6 +1115,8 @@ type GenericFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, interface{}) error } GenericFlag is a flag with type Generic @@ -1181,6 +1183,8 @@ type Int64Flag struct { EnvVars []string Base int + + Action func(*Context, int64) error } Int64Flag is a flag with type int64 @@ -1267,6 +1271,8 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int64) error } Int64SliceFlag is a flag with type *Int64Slice @@ -1338,6 +1344,8 @@ type IntFlag struct { EnvVars []string Base int + + Action func(*Context, int) error } IntFlag is a flag with type int @@ -1428,6 +1436,8 @@ type IntSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []int) error } IntSliceFlag is a flag with type *IntSlice @@ -1533,6 +1543,8 @@ type PathFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, Path) error } PathFlag is a flag with type Path @@ -1673,6 +1685,8 @@ type StringFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, string) error } StringFlag is a flag with type string @@ -1761,6 +1775,8 @@ type StringSliceFlag struct { EnvVars []string TakesFile bool + + Action func(*Context, []string) error } StringSliceFlag is a flag with type *StringSlice @@ -1867,6 +1883,8 @@ type TimestampFlag struct { Layout string Timezone *time.Location + + Action func(*Context, *time.Time) error } TimestampFlag is a flag with type *Timestamp @@ -1932,6 +1950,8 @@ type Uint64Flag struct { EnvVars []string Base int + + Action func(*Context, uint64) error } Uint64Flag is a flag with type uint64 @@ -2018,6 +2038,8 @@ type Uint64SliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint64) error } Uint64SliceFlag is a flag with type *Uint64Slice @@ -2080,6 +2102,8 @@ type UintFlag struct { EnvVars []string Base int + + Action func(*Context, uint) error } UintFlag is a flag with type uint @@ -2170,6 +2194,8 @@ type UintSliceFlag struct { Aliases []string EnvVars []string + + Action func(*Context, []uint) error } UintSliceFlag is a flag with type *UintSlice