Merge pull request #946 from unRob/fix/issue-944/ctx-narg-panics-during-custom-bashcomplete
Prevent panic on flagSet access from custom BashComplete
This commit is contained in:
commit
796ffdc72a
4
app.go
4
app.go
@ -223,7 +223,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = parseIter(set, a, arguments[1:])
|
err = parseIter(set, a, arguments[1:], shellComplete)
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
context := NewContext(a, set, nil)
|
context := NewContext(a, set, nil)
|
||||||
if nerr != nil {
|
if nerr != nil {
|
||||||
@ -349,7 +349,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = parseIter(set, a, ctx.Args().Tail())
|
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
context := NewContext(a, set, ctx)
|
context := NewContext(a, set, ctx)
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
c.UseShortOptionHandling = true
|
c.UseShortOptionHandling = true
|
||||||
}
|
}
|
||||||
|
|
||||||
set, err := c.parseFlags(ctx.Args())
|
set, err := c.parseFlags(ctx.Args(), ctx.shellComplete)
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
context := NewContext(ctx.App, set, ctx)
|
||||||
context.Command = c
|
context.Command = c
|
||||||
@ -174,7 +174,7 @@ func (c *Command) useShortOptionHandling() bool {
|
|||||||
return c.UseShortOptionHandling
|
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()
|
set, err := c.newFlagSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -184,7 +184,7 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
|
|||||||
return set, set.Parse(append([]string{"--"}, args.Tail()...))
|
return set, set.Parse(append([]string{"--"}, args.Tail()...))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = parseIter(set, c, args.Tail())
|
err = parseIter(set, c, args.Tail(), shellComplete)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -329,3 +330,49 @@ func TestCommandSkipFlagParsing(t *testing.T) {
|
|||||||
expect(t, args, c.expectedArgs)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
9
parse.go
9
parse.go
@ -11,13 +11,18 @@ type iterativeParser interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
|
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
|
||||||
// iteratively catch parsing errors. This way we achieve LR parsing without
|
// iteratively catch parsing errors. This way we achieve LR parsing without
|
||||||
// transforming any arguments. Otherwise, there is no way we can discriminate
|
// transforming any arguments. Otherwise, there is no way we can discriminate
|
||||||
// combined short options from common arguments that should be left untouched.
|
// combined short options from common arguments that should be left untouched.
|
||||||
func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error {
|
// Pass `shellComplete` to continue parsing options on failure during shell
|
||||||
|
// completion when, the user-supplied options may be incomplete.
|
||||||
|
func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error {
|
||||||
for {
|
for {
|
||||||
err := set.Parse(args)
|
err := set.Parse(args)
|
||||||
if !ip.useShortOptionHandling() || err == nil {
|
if !ip.useShortOptionHandling() || err == nil {
|
||||||
|
if shellComplete {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user