]> Cypherpunks repositories - gostls13.git/commitdiff
flag: test IsBoolFlag when creating the usage message
authorRob Pike <r@golang.org>
Sun, 18 Sep 2022 12:05:00 +0000 (22:05 +1000)
committerRob Pike <r@golang.org>
Mon, 19 Sep 2022 21:54:35 +0000 (21:54 +0000)
Although I can't think of any reason to do this, it is possible for
a user-defined flag to implement IsBoolFlag but return "false".
This is nuts because checking the interface is satisfied should
obviously be sufficient, but the documentation kinda implies it's
not. And if you try this, you'll discover that the usage message
ignores the return value even though the rest of the package plays
nice. Bother.

So we fix it, as the fix is trivial: call the method when creating
the usage message.

Fixes #53473

Change-Id: I1ac80a876ad5626eebfc5ef6cb972cd3007afaad
Reviewed-on: https://go-review.googlesource.com/c/go/+/431102
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/flag/flag.go
src/flag/flag_test.go

index 9abf8d769eafdf1299eecdb2662bbdac873fde7a..f6b38908f6d7e90af49bb8b7aaff4c100d6719d7 100644 (file)
@@ -550,9 +550,11 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
        }
        // No explicit name, so use type if we can find one.
        name = "value"
-       switch flag.Value.(type) {
+       switch fv := flag.Value.(type) {
        case boolFlag:
-               name = ""
+               if fv.IsBoolFlag() {
+                       name = ""
+               }
        case *durationValue:
                name = "duration"
        case *float64Value:
index 791a8826bedde53064b0a4c5d6f05985785ca9c0..17551684055cbdd3ce3b494c6750fe66df51ba3a 100644 (file)
@@ -5,6 +5,7 @@
 package flag_test
 
 import (
+       "bytes"
        . "flag"
        "fmt"
        "internal/testenv"
@@ -355,6 +356,31 @@ func TestUserDefinedBool(t *testing.T) {
        }
 }
 
+func TestUserDefinedBoolUsage(t *testing.T) {
+       var flags FlagSet
+       flags.Init("test", ContinueOnError)
+       var buf bytes.Buffer
+       flags.SetOutput(&buf)
+       var b boolFlagVar
+       flags.Var(&b, "b", "X")
+       b.count = 0
+       // b.IsBoolFlag() will return true and usage will look boolean.
+       flags.PrintDefaults()
+       got := buf.String()
+       want := "  -b\tX\n"
+       if got != want {
+               t.Errorf("false: want %q; got %q", want, got)
+       }
+       b.count = 4
+       // b.IsBoolFlag() will return false and usage will look non-boolean.
+       flags.PrintDefaults()
+       got = buf.String()
+       want = "  -b\tX\n  -b value\n    \tX\n"
+       if got != want {
+               t.Errorf("false: want %q; got %q", want, got)
+       }
+}
+
 func TestSetOutput(t *testing.T) {
        var flags FlagSet
        var buf strings.Builder