cmd/stringer: add -trimprefix option

To trim a string prefix from the names when generating their final
strings. Add a simple test too.

There is no automatic detection of prefixes for now. That can be added
later, building on top of this first simple implementation.

Fixes #16539.

Change-Id: Ica37273ac74bb0a6cbd43e61823786963d86a492
Reviewed-on: https://go-review.googlesource.com/76650
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/cmd/stringer/golden_test.go b/cmd/stringer/golden_test.go
index 12df238..166fc03 100644
--- a/cmd/stringer/golden_test.go
+++ b/cmd/stringer/golden_test.go
@@ -16,18 +16,20 @@
 
 // Golden represents a test case.
 type Golden struct {
-	name   string
-	input  string // input; the package clause is provided when running the test.
-	output string // exected output.
+	name       string
+	trimPrefix string
+	input      string // input; the package clause is provided when running the test.
+	output     string // exected output.
 }
 
 var golden = []Golden{
-	{"day", day_in, day_out},
-	{"offset", offset_in, offset_out},
-	{"gap", gap_in, gap_out},
-	{"num", num_in, num_out},
-	{"unum", unum_in, unum_out},
-	{"prime", prime_in, prime_out},
+	{"day", "", day_in, day_out},
+	{"offset", "", offset_in, offset_out},
+	{"gap", "", gap_in, gap_out},
+	{"num", "", num_in, num_out},
+	{"unum", "", unum_in, unum_out},
+	{"prime", "", prime_in, prime_out},
+	{"prefix", "Type", prefix_in, prefix_out},
 }
 
 // Each example starts with "type XXX [u]int", with a single space separating them.
@@ -238,9 +240,34 @@
 }
 `
 
+const prefix_in = `type Type int
+const (
+	TypeInt Type = iota
+	TypeString
+	TypeFloat
+	TypeRune
+	TypeByte
+	TypeStruct
+	TypeSlice
+)
+`
+
+const prefix_out = `
+const _Type_name = "IntStringFloatRuneByteStructSlice"
+
+var _Type_index = [...]uint8{0, 3, 9, 14, 18, 22, 28, 33}
+
+func (i Type) String() string {
+	if i < 0 || i >= Type(len(_Type_index)-1) {
+		return fmt.Sprintf("Type(%d)", i)
+	}
+	return _Type_name[_Type_index[i]:_Type_index[i+1]]
+}
+`
+
 func TestGolden(t *testing.T) {
 	for _, test := range golden {
-		var g Generator
+		g := Generator{trimPrefix: test.trimPrefix}
 		input := "package test\n" + test.input
 		file := test.name + ".go"
 		g.parsePackage(".", []string{file}, input)
diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go
index f741d98..2ae36e8 100644
--- a/cmd/stringer/stringer.go
+++ b/cmd/stringer/stringer.go
@@ -78,8 +78,9 @@
 )
 
 var (
-	typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
-	output    = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
+	typeNames  = flag.String("type", "", "comma-separated list of type names; must be set")
+	output     = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
+	trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
 )
 
 // Usage is a replacement usage function for the flags package.
@@ -112,10 +113,8 @@
 	}
 
 	// Parse the package once.
-	var (
-		dir string
-		g   Generator
-	)
+	var dir string
+	g := Generator{trimPrefix: *trimprefix}
 	if len(args) == 1 && isDirectory(args[0]) {
 		dir = args[0]
 		g.parsePackageDir(args[0])
@@ -165,6 +164,8 @@
 type Generator struct {
 	buf bytes.Buffer // Accumulated output.
 	pkg *Package     // Package we are scanning.
+
+	trimPrefix string
 }
 
 func (g *Generator) Printf(format string, args ...interface{}) {
@@ -178,6 +179,8 @@
 	// These fields are reset for each type being generated.
 	typeName string  // Name of the constant type.
 	values   []Value // Accumulator for constant values of that type.
+
+	trimPrefix string
 }
 
 type Package struct {
@@ -240,8 +243,9 @@
 		}
 		astFiles = append(astFiles, parsedFile)
 		files = append(files, &File{
-			file: parsedFile,
-			pkg:  g.pkg,
+			file:       parsedFile,
+			pkg:        g.pkg,
+			trimPrefix: g.trimPrefix,
 		})
 	}
 	if len(astFiles) == 0 {
@@ -453,6 +457,7 @@
 				signed: info&types.IsUnsigned == 0,
 				str:    value.String(),
 			}
+			v.name = strings.TrimPrefix(v.name, f.trimPrefix)
 			f.values = append(f.values, v)
 		}
 	}