cmd/stringer: add flag to use line comment as str

This can be very helpful if you lay out each value's string
representation like this:

	and    // &
	andAnd // &&
	or     // |
	orOr   // ||

Without the use of comments, it's impossible to use stringer with these
names as the characters & and | cannot form valid identifiers in a Go
program.

Fixes #20483.

Change-Id: I4d36c74059dd48ae3a5e09b70a429a75853ef179
Reviewed-on: https://go-review.googlesource.com/44076
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Daniel Martí 2017-11-19 20:52:17 +00:00
parent 64890f4e2b
commit ae8cc59455
2 changed files with 67 additions and 22 deletions

View File

@ -18,18 +18,20 @@ import (
type Golden struct { type Golden struct {
name string name string
trimPrefix string trimPrefix string
lineComment bool
input string // input; the package clause is provided when running the test. input string // input; the package clause is provided when running the test.
output string // exected output. output string // exected output.
} }
var golden = []Golden{ var golden = []Golden{
{"day", "", day_in, day_out}, {"day", "", false, day_in, day_out},
{"offset", "", offset_in, offset_out}, {"offset", "", false, offset_in, offset_out},
{"gap", "", gap_in, gap_out}, {"gap", "", false, gap_in, gap_out},
{"num", "", num_in, num_out}, {"num", "", false, num_in, num_out},
{"unum", "", unum_in, unum_out}, {"unum", "", false, unum_in, unum_out},
{"prime", "", prime_in, prime_out}, {"prime", "", false, prime_in, prime_out},
{"prefix", "Type", prefix_in, prefix_out}, {"prefix", "Type", false, prefix_in, prefix_out},
{"tokens", "", true, tokens_in, tokens_out},
} }
// Each example starts with "type XXX [u]int", with a single space separating them. // Each example starts with "type XXX [u]int", with a single space separating them.
@ -264,9 +266,42 @@ func (i Type) String() string {
} }
` `
const tokens_in = `type Token int
const (
And Token = iota // &
Or // |
Add // +
Sub // -
Ident
Period // .
// not to be used
SingleBefore
// not to be used
BeforeAndInline // inline
InlineGeneral /* inline general */
)
`
const tokens_out = `
const _Token_name = "&|+-Ident.SingleBeforeinlineinline general"
var _Token_index = [...]uint8{0, 1, 2, 3, 4, 9, 10, 22, 28, 42}
func (i Token) String() string {
if i < 0 || i >= Token(len(_Token_index)-1) {
return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Token_name[_Token_index[i]:_Token_index[i+1]]
}
`
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for _, test := range golden { for _, test := range golden {
g := Generator{trimPrefix: test.trimPrefix} g := Generator{
trimPrefix: test.trimPrefix,
lineComment: test.lineComment,
}
input := "package test\n" + test.input input := "package test\n" + test.input
file := test.name + ".go" file := test.name + ".go"
g.parsePackage(".", []string{file}, input) g.parsePackage(".", []string{file}, input)

View File

@ -81,6 +81,7 @@ var (
typeNames = flag.String("type", "", "comma-separated list of type names; must be set") 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") output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
) )
// Usage is a replacement usage function for the flags package. // Usage is a replacement usage function for the flags package.
@ -114,7 +115,10 @@ func main() {
// Parse the package once. // Parse the package once.
var dir string var dir string
g := Generator{trimPrefix: *trimprefix} g := Generator{
trimPrefix: *trimprefix,
lineComment: *linecomment,
}
if len(args) == 1 && isDirectory(args[0]) { if len(args) == 1 && isDirectory(args[0]) {
dir = args[0] dir = args[0]
g.parsePackageDir(args[0]) g.parsePackageDir(args[0])
@ -166,6 +170,7 @@ type Generator struct {
pkg *Package // Package we are scanning. pkg *Package // Package we are scanning.
trimPrefix string trimPrefix string
lineComment bool
} }
func (g *Generator) Printf(format string, args ...interface{}) { func (g *Generator) Printf(format string, args ...interface{}) {
@ -181,6 +186,7 @@ type File struct {
values []Value // Accumulator for constant values of that type. values []Value // Accumulator for constant values of that type.
trimPrefix string trimPrefix string
lineComment bool
} }
type Package struct { type Package struct {
@ -237,7 +243,7 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac
if !strings.HasSuffix(name, ".go") { if !strings.HasSuffix(name, ".go") {
continue continue
} }
parsedFile, err := parser.ParseFile(fs, name, text, 0) parsedFile, err := parser.ParseFile(fs, name, text, parser.ParseComments)
if err != nil { if err != nil {
log.Fatalf("parsing package: %s: %s", name, err) log.Fatalf("parsing package: %s: %s", name, err)
} }
@ -246,6 +252,7 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac
file: parsedFile, file: parsedFile,
pkg: g.pkg, pkg: g.pkg,
trimPrefix: g.trimPrefix, trimPrefix: g.trimPrefix,
lineComment: g.lineComment,
}) })
} }
if len(astFiles) == 0 { if len(astFiles) == 0 {
@ -457,6 +464,9 @@ func (f *File) genDecl(node ast.Node) bool {
signed: info&types.IsUnsigned == 0, signed: info&types.IsUnsigned == 0,
str: value.String(), str: value.String(),
} }
if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
v.name = strings.TrimSpace(c.Text())
}
v.name = strings.TrimPrefix(v.name, f.trimPrefix) v.name = strings.TrimPrefix(v.name, f.trimPrefix)
f.values = append(f.values, v) f.values = append(f.values, v)
} }