From 90a62d7b0c1620a167002eb10a5e2f13924272de Mon Sep 17 00:00:00 2001 From: Roberto Hidalgo Date: Mon, 25 Nov 2019 14:25:11 -0500 Subject: [PATCH] Prevent panic on flagSet access from custom BashComplete Fixes #944 --- command.go | 7 ++++--- command_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/command.go b/command.go index a579fdb..a99da0a 100644 --- a/command.go +++ b/command.go @@ -100,7 +100,7 @@ func (c *Command) Run(ctx *Context) (err error) { c.UseShortOptionHandling = true } - set, err := c.parseFlags(ctx.Args()) + set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) context := NewContext(ctx.App, set, ctx) context.Command = c @@ -174,7 +174,7 @@ func (c *Command) useShortOptionHandling() bool { return c.UseShortOptionHandling } -func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { +func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { set, err := c.newFlagSet() if err != nil { return nil, err @@ -185,7 +185,8 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { } err = parseIter(set, c, args.Tail()) - if err != nil { + // Continue parsing flags on failure during shell completion + if err != nil && !shellComplete { return nil, err } diff --git a/command_test.go b/command_test.go index 7c55f13..8eafe68 100644 --- a/command_test.go +++ b/command_test.go @@ -1,6 +1,7 @@ package cli import ( + "bytes" "errors" "flag" "fmt" @@ -329,3 +330,49 @@ func TestCommandSkipFlagParsing(t *testing.T) { expect(t, args, c.expectedArgs) } } + +func TestCommand_Run_CustomShellCompleteAcceptsMalformedFlags(t *testing.T) { + cases := []struct { + testArgs args + expectedOut string + }{ + {testArgs: args{"--undefined"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "fourty-two"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "42"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "42", "newArg"}, expectedOut: "found 1 args"}, + } + + for _, c := range cases { + var outputBuffer bytes.Buffer + app := &App{ + Writer: &outputBuffer, + EnableBashCompletion: true, + Commands: []*Command{ + { + Name: "bar", + Usage: "this is for testing", + Flags: []Flag{ + &IntFlag{ + Name: "number", + Usage: "A number to parse", + }, + }, + BashComplete: func(c *Context) { + fmt.Fprintf(c.App.Writer, "found %d args", c.NArg()) + }, + }, + }, + } + + osArgs := args{"foo", "bar"} + osArgs = append(osArgs, c.testArgs...) + osArgs = append(osArgs, "--generate-bash-completion") + + err := app.Run(osArgs) + stdout := outputBuffer.String() + expect(t, err, nil) + expect(t, stdout, c.expectedOut) + } + +}