diff options
Diffstat (limited to 'libgo/go/flag')
-rw-r--r-- | libgo/go/flag/flag.go | 44 | ||||
-rw-r--r-- | libgo/go/flag/flag_test.go | 83 |
2 files changed, 112 insertions, 15 deletions
diff --git a/libgo/go/flag/flag.go b/libgo/go/flag/flag.go index a8485f0..885a4c8 100644 --- a/libgo/go/flag/flag.go +++ b/libgo/go/flag/flag.go @@ -508,31 +508,33 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { // documentation for the global function PrintDefaults for more information. func (f *FlagSet) PrintDefaults() { f.VisitAll(func(flag *Flag) { - s := fmt.Sprintf(" -%s", flag.Name) // Two spaces before -; see next two comments. + var b strings.Builder + fmt.Fprintf(&b, " -%s", flag.Name) // Two spaces before -; see next two comments. name, usage := UnquoteUsage(flag) if len(name) > 0 { - s += " " + name + b.WriteString(" ") + b.WriteString(name) } // Boolean flags of one ASCII letter are so common we // treat them specially, putting their usage on the same line. - if len(s) <= 4 { // space, space, '-', 'x'. - s += "\t" + if b.Len() <= 4 { // space, space, '-', 'x'. + b.WriteString("\t") } else { // Four spaces before the tab triggers good alignment // for both 4- and 8-space tab stops. - s += "\n \t" + b.WriteString("\n \t") } - s += strings.ReplaceAll(usage, "\n", "\n \t") + b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t")) if !isZeroValue(flag, flag.DefValue) { if _, ok := flag.Value.(*stringValue); ok { // put quotes on the value - s += fmt.Sprintf(" (default %q)", flag.DefValue) + fmt.Fprintf(&b, " (default %q)", flag.DefValue) } else { - s += fmt.Sprintf(" (default %v)", flag.DefValue) + fmt.Fprintf(&b, " (default %v)", flag.DefValue) } } - fmt.Fprint(f.Output(), s, "\n") + fmt.Fprint(f.Output(), b.String(), "\n") }) } @@ -857,17 +859,23 @@ func Func(name, usage string, fn func(string) error) { // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func (f *FlagSet) Var(value Value, name string, usage string) { + // Flag must not begin "-" or contain "=". + if strings.HasPrefix(name, "-") { + panic(f.sprintf("flag %q begins with -", name)) + } else if strings.Contains(name, "=") { + panic(f.sprintf("flag %q contains =", name)) + } + // Remember the default value as a string; it won't change. flag := &Flag{name, usage, value, value.String()} _, alreadythere := f.formal[name] if alreadythere { var msg string if f.name == "" { - msg = fmt.Sprintf("flag redefined: %s", name) + msg = f.sprintf("flag redefined: %s", name) } else { - msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) + msg = f.sprintf("%s flag redefined: %s", f.name, name) } - fmt.Fprintln(f.Output(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { @@ -886,13 +894,19 @@ func Var(value Value, name string, usage string) { CommandLine.Var(value, name, usage) } +// sprintf formats the message, prints it to output, and returns it. +func (f *FlagSet) sprintf(format string, a ...interface{}) string { + msg := fmt.Sprintf(format, a...) + fmt.Fprintln(f.Output(), msg) + return msg +} + // failf prints to standard error a formatted error and usage message and // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { - err := fmt.Errorf(format, a...) - fmt.Fprintln(f.Output(), err) + msg := f.sprintf(format, a...) f.usage() - return err + return errors.New(msg) } // usage calls the Usage method for the flag set if one is specified, diff --git a/libgo/go/flag/flag_test.go b/libgo/go/flag/flag_test.go index 06cab79..5835fcf 100644 --- a/libgo/go/flag/flag_test.go +++ b/libgo/go/flag/flag_test.go @@ -655,3 +655,86 @@ func TestExitCode(t *testing.T) { } } } + +func mustPanic(t *testing.T, testName string, expected string, f func()) { + t.Helper() + defer func() { + switch msg := recover().(type) { + case nil: + t.Errorf("%s\n: expected panic(%q), but did not panic", testName, expected) + case string: + if msg != expected { + t.Errorf("%s\n: expected panic(%q), but got panic(%q)", testName, expected, msg) + } + default: + t.Errorf("%s\n: expected panic(%q), but got panic(%T%v)", testName, expected, msg, msg) + } + }() + f() +} + +func TestInvalidFlags(t *testing.T) { + tests := []struct { + flag string + errorMsg string + }{ + { + flag: "-foo", + errorMsg: "flag \"-foo\" begins with -", + }, + { + flag: "foo=bar", + errorMsg: "flag \"foo=bar\" contains =", + }, + } + + for _, test := range tests { + testName := fmt.Sprintf("FlagSet.Var(&v, %q, \"\")", test.flag) + + fs := NewFlagSet("", ContinueOnError) + buf := bytes.NewBuffer(nil) + fs.SetOutput(buf) + + mustPanic(t, testName, test.errorMsg, func() { + var v flagVar + fs.Var(&v, test.flag, "") + }) + if msg := test.errorMsg + "\n"; msg != buf.String() { + t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf) + } + } +} + +func TestRedefinedFlags(t *testing.T) { + tests := []struct { + flagSetName string + errorMsg string + }{ + { + flagSetName: "", + errorMsg: "flag redefined: foo", + }, + { + flagSetName: "fs", + errorMsg: "fs flag redefined: foo", + }, + } + + for _, test := range tests { + testName := fmt.Sprintf("flag redefined in FlagSet(%q)", test.flagSetName) + + fs := NewFlagSet(test.flagSetName, ContinueOnError) + buf := bytes.NewBuffer(nil) + fs.SetOutput(buf) + + var v flagVar + fs.Var(&v, "foo", "") + + mustPanic(t, testName, test.errorMsg, func() { + fs.Var(&v, "foo", "") + }) + if msg := test.errorMsg + "\n"; msg != buf.String() { + t.Errorf("%s\n: unexpected output: expected %q, bug got %q", testName, msg, buf) + } + } +} |