1// Copyright 2014 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer 6// interface. Given the name of a (signed or unsigned) integer type T that has constants 7// defined, stringer will create a new self-contained Go source file implementing 8// func (t T) String() string 9// The file is created in the same package and directory as the package that defines T. 10// It has helpful defaults designed for use with go generate. 11// 12// Stringer works best with constants that are consecutive values such as created using iota, 13// but creates good code regardless. In the future it might also provide custom support for 14// constant sets that are bit patterns. 15// 16// For example, given this snippet, 17// 18// package painkiller 19// 20// type Pill int 21// 22// const ( 23// Placebo Pill = iota 24// Aspirin 25// Ibuprofen 26// Paracetamol 27// Acetaminophen = Paracetamol 28// ) 29// 30// running this command 31// 32// stringer -type=Pill 33// 34// in the same directory will create the file pill_string.go, in package painkiller, 35// containing a definition of 36// 37// func (Pill) String() string 38// 39// That method will translate the value of a Pill constant to the string representation 40// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will 41// print the string "Aspirin". 42// 43// Typically this process would be run using go generate, like this: 44// 45// //go:generate stringer -type=Pill 46// 47// If multiple constants have the same value, the lexically first matching name will 48// be used (in the example, Acetaminophen will print as "Paracetamol"). 49// 50// With no arguments, it processes the package in the current directory. 51// Otherwise, the arguments must name a single directory holding a Go package 52// or a set of Go source files that represent a single Go package. 53// 54// The -type flag accepts a comma-separated list of types so a single run can 55// generate methods for multiple types. The default output file is t_string.go, 56// where t is the lower-cased name of the first type listed. It can be overridden 57// with the -output flag. 58// 59// The -linecomment flag tells stringer to generate the text of any line comment, trimmed 60// of leading spaces, instead of the constant name. For instance, if the constants above had a 61// Pill prefix, one could write 62// PillAspirin // Aspirin 63// to suppress it in the output. 64 65package main // import "golang.org/x/tools/cmd/stringer" 66 67import ( 68 "bytes" 69 "flag" 70 "fmt" 71 "go/ast" 72 "go/constant" 73 "go/format" 74 "go/token" 75 "go/types" 76 "io/ioutil" 77 "log" 78 "os" 79 "path/filepath" 80 "sort" 81 "strings" 82 83 "golang.org/x/tools/go/packages" 84) 85 86var ( 87 typeNames = flag.String("type", "", "comma-separated list of type names; must be set") 88 output = flag.String("output", "", "output file name; default srcdir/<type>_string.go") 89 trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") 90 linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") 91 buildTags = flag.String("tags", "", "comma-separated list of build tags to apply") 92) 93 94// Usage is a replacement usage function for the flags package. 95func Usage() { 96 fmt.Fprintf(os.Stderr, "Usage of stringer:\n") 97 fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n") 98 fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n") 99 fmt.Fprintf(os.Stderr, "For more information, see:\n") 100 fmt.Fprintf(os.Stderr, "\thttp://godoc.org/golang.org/x/tools/cmd/stringer\n") 101 fmt.Fprintf(os.Stderr, "Flags:\n") 102 flag.PrintDefaults() 103} 104 105func main() { 106 log.SetFlags(0) 107 log.SetPrefix("stringer: ") 108 flag.Usage = Usage 109 flag.Parse() 110 if len(*typeNames) == 0 { 111 flag.Usage() 112 os.Exit(2) 113 } 114 types := strings.Split(*typeNames, ",") 115 var tags []string 116 if len(*buildTags) > 0 { 117 tags = strings.Split(*buildTags, ",") 118 } 119 120 // We accept either one directory or a list of files. Which do we have? 121 args := flag.Args() 122 if len(args) == 0 { 123 // Default: process whole package in current directory. 124 args = []string{"."} 125 } 126 127 // Parse the package once. 128 var dir string 129 g := Generator{ 130 trimPrefix: *trimprefix, 131 lineComment: *linecomment, 132 } 133 // TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc). 134 if len(args) == 1 && isDirectory(args[0]) { 135 dir = args[0] 136 } else { 137 if len(tags) != 0 { 138 log.Fatal("-tags option applies only to directories, not when files are specified") 139 } 140 dir = filepath.Dir(args[0]) 141 } 142 143 g.parsePackage(args, tags) 144 145 // Print the header and package clause. 146 g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) 147 g.Printf("\n") 148 g.Printf("package %s", g.pkg.name) 149 g.Printf("\n") 150 g.Printf("import \"strconv\"\n") // Used by all methods. 151 152 // Run generate for each type. 153 for _, typeName := range types { 154 g.generate(typeName) 155 } 156 157 // Format the output. 158 src := g.format() 159 160 // Write to file. 161 outputName := *output 162 if outputName == "" { 163 baseName := fmt.Sprintf("%s_string.go", types[0]) 164 outputName = filepath.Join(dir, strings.ToLower(baseName)) 165 } 166 err := ioutil.WriteFile(outputName, src, 0644) 167 if err != nil { 168 log.Fatalf("writing output: %s", err) 169 } 170} 171 172// isDirectory reports whether the named file is a directory. 173func isDirectory(name string) bool { 174 info, err := os.Stat(name) 175 if err != nil { 176 log.Fatal(err) 177 } 178 return info.IsDir() 179} 180 181// Generator holds the state of the analysis. Primarily used to buffer 182// the output for format.Source. 183type Generator struct { 184 buf bytes.Buffer // Accumulated output. 185 pkg *Package // Package we are scanning. 186 187 trimPrefix string 188 lineComment bool 189} 190 191func (g *Generator) Printf(format string, args ...interface{}) { 192 fmt.Fprintf(&g.buf, format, args...) 193} 194 195// File holds a single parsed file and associated data. 196type File struct { 197 pkg *Package // Package to which this file belongs. 198 file *ast.File // Parsed AST. 199 // These fields are reset for each type being generated. 200 typeName string // Name of the constant type. 201 values []Value // Accumulator for constant values of that type. 202 203 trimPrefix string 204 lineComment bool 205} 206 207type Package struct { 208 name string 209 defs map[*ast.Ident]types.Object 210 files []*File 211} 212 213// parsePackage analyzes the single package constructed from the patterns and tags. 214// parsePackage exits if there is an error. 215func (g *Generator) parsePackage(patterns []string, tags []string) { 216 cfg := &packages.Config{ 217 Mode: packages.LoadSyntax, 218 // TODO: Need to think about constants in test files. Maybe write type_string_test.go 219 // in a separate pass? For later. 220 Tests: false, 221 BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, 222 } 223 pkgs, err := packages.Load(cfg, patterns...) 224 if err != nil { 225 log.Fatal(err) 226 } 227 if len(pkgs) != 1 { 228 log.Fatalf("error: %d packages found", len(pkgs)) 229 } 230 g.addPackage(pkgs[0]) 231} 232 233// addPackage adds a type checked Package and its syntax files to the generator. 234func (g *Generator) addPackage(pkg *packages.Package) { 235 g.pkg = &Package{ 236 name: pkg.Name, 237 defs: pkg.TypesInfo.Defs, 238 files: make([]*File, len(pkg.Syntax)), 239 } 240 241 for i, file := range pkg.Syntax { 242 g.pkg.files[i] = &File{ 243 file: file, 244 pkg: g.pkg, 245 trimPrefix: g.trimPrefix, 246 lineComment: g.lineComment, 247 } 248 } 249} 250 251// generate produces the String method for the named type. 252func (g *Generator) generate(typeName string) { 253 values := make([]Value, 0, 100) 254 for _, file := range g.pkg.files { 255 // Set the state for this run of the walker. 256 file.typeName = typeName 257 file.values = nil 258 if file.file != nil { 259 ast.Inspect(file.file, file.genDecl) 260 values = append(values, file.values...) 261 } 262 } 263 264 if len(values) == 0 { 265 log.Fatalf("no values defined for type %s", typeName) 266 } 267 // Generate code that will fail if the constants change value. 268 g.Printf("func _() {\n") 269 g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n") 270 g.Printf("\t// Re-run the stringer command to generate them again.\n") 271 g.Printf("\tvar x [1]struct{}\n") 272 for _, v := range values { 273 g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str) 274 } 275 g.Printf("}\n") 276 runs := splitIntoRuns(values) 277 // The decision of which pattern to use depends on the number of 278 // runs in the numbers. If there's only one, it's easy. For more than 279 // one, there's a tradeoff between complexity and size of the data 280 // and code vs. the simplicity of a map. A map takes more space, 281 // but so does the code. The decision here (crossover at 10) is 282 // arbitrary, but considers that for large numbers of runs the cost 283 // of the linear scan in the switch might become important, and 284 // rather than use yet another algorithm such as binary search, 285 // we punt and use a map. In any case, the likelihood of a map 286 // being necessary for any realistic example other than bitmasks 287 // is very low. And bitmasks probably deserve their own analysis, 288 // to be done some other day. 289 switch { 290 case len(runs) == 1: 291 g.buildOneRun(runs, typeName) 292 case len(runs) <= 10: 293 g.buildMultipleRuns(runs, typeName) 294 default: 295 g.buildMap(runs, typeName) 296 } 297} 298 299// splitIntoRuns breaks the values into runs of contiguous sequences. 300// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}. 301// The input slice is known to be non-empty. 302func splitIntoRuns(values []Value) [][]Value { 303 // We use stable sort so the lexically first name is chosen for equal elements. 304 sort.Stable(byValue(values)) 305 // Remove duplicates. Stable sort has put the one we want to print first, 306 // so use that one. The String method won't care about which named constant 307 // was the argument, so the first name for the given value is the only one to keep. 308 // We need to do this because identical values would cause the switch or map 309 // to fail to compile. 310 j := 1 311 for i := 1; i < len(values); i++ { 312 if values[i].value != values[i-1].value { 313 values[j] = values[i] 314 j++ 315 } 316 } 317 values = values[:j] 318 runs := make([][]Value, 0, 10) 319 for len(values) > 0 { 320 // One contiguous sequence per outer loop. 321 i := 1 322 for i < len(values) && values[i].value == values[i-1].value+1 { 323 i++ 324 } 325 runs = append(runs, values[:i]) 326 values = values[i:] 327 } 328 return runs 329} 330 331// format returns the gofmt-ed contents of the Generator's buffer. 332func (g *Generator) format() []byte { 333 src, err := format.Source(g.buf.Bytes()) 334 if err != nil { 335 // Should never happen, but can arise when developing this code. 336 // The user can compile the output to see the error. 337 log.Printf("warning: internal error: invalid Go generated: %s", err) 338 log.Printf("warning: compile the package to analyze the error") 339 return g.buf.Bytes() 340 } 341 return src 342} 343 344// Value represents a declared constant. 345type Value struct { 346 originalName string // The name of the constant. 347 name string // The name with trimmed prefix. 348 // The value is stored as a bit pattern alone. The boolean tells us 349 // whether to interpret it as an int64 or a uint64; the only place 350 // this matters is when sorting. 351 // Much of the time the str field is all we need; it is printed 352 // by Value.String. 353 value uint64 // Will be converted to int64 when needed. 354 signed bool // Whether the constant is a signed type. 355 str string // The string representation given by the "go/constant" package. 356} 357 358func (v *Value) String() string { 359 return v.str 360} 361 362// byValue lets us sort the constants into increasing order. 363// We take care in the Less method to sort in signed or unsigned order, 364// as appropriate. 365type byValue []Value 366 367func (b byValue) Len() int { return len(b) } 368func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 369func (b byValue) Less(i, j int) bool { 370 if b[i].signed { 371 return int64(b[i].value) < int64(b[j].value) 372 } 373 return b[i].value < b[j].value 374} 375 376// genDecl processes one declaration clause. 377func (f *File) genDecl(node ast.Node) bool { 378 decl, ok := node.(*ast.GenDecl) 379 if !ok || decl.Tok != token.CONST { 380 // We only care about const declarations. 381 return true 382 } 383 // The name of the type of the constants we are declaring. 384 // Can change if this is a multi-element declaration. 385 typ := "" 386 // Loop over the elements of the declaration. Each element is a ValueSpec: 387 // a list of names possibly followed by a type, possibly followed by values. 388 // If the type and value are both missing, we carry down the type (and value, 389 // but the "go/types" package takes care of that). 390 for _, spec := range decl.Specs { 391 vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. 392 if vspec.Type == nil && len(vspec.Values) > 0 { 393 // "X = 1". With no type but a value. If the constant is untyped, 394 // skip this vspec and reset the remembered type. 395 typ = "" 396 397 // If this is a simple type conversion, remember the type. 398 // We don't mind if this is actually a call; a qualified call won't 399 // be matched (that will be SelectorExpr, not Ident), and only unusual 400 // situations will result in a function call that appears to be 401 // a type conversion. 402 ce, ok := vspec.Values[0].(*ast.CallExpr) 403 if !ok { 404 continue 405 } 406 id, ok := ce.Fun.(*ast.Ident) 407 if !ok { 408 continue 409 } 410 typ = id.Name 411 } 412 if vspec.Type != nil { 413 // "X T". We have a type. Remember it. 414 ident, ok := vspec.Type.(*ast.Ident) 415 if !ok { 416 continue 417 } 418 typ = ident.Name 419 } 420 if typ != f.typeName { 421 // This is not the type we're looking for. 422 continue 423 } 424 // We now have a list of names (from one line of source code) all being 425 // declared with the desired type. 426 // Grab their names and actual values and store them in f.values. 427 for _, name := range vspec.Names { 428 if name.Name == "_" { 429 continue 430 } 431 // This dance lets the type checker find the values for us. It's a 432 // bit tricky: look up the object declared by the name, find its 433 // types.Const, and extract its value. 434 obj, ok := f.pkg.defs[name] 435 if !ok { 436 log.Fatalf("no value for constant %s", name) 437 } 438 info := obj.Type().Underlying().(*types.Basic).Info() 439 if info&types.IsInteger == 0 { 440 log.Fatalf("can't handle non-integer constant type %s", typ) 441 } 442 value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. 443 if value.Kind() != constant.Int { 444 log.Fatalf("can't happen: constant is not an integer %s", name) 445 } 446 i64, isInt := constant.Int64Val(value) 447 u64, isUint := constant.Uint64Val(value) 448 if !isInt && !isUint { 449 log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) 450 } 451 if !isInt { 452 u64 = uint64(i64) 453 } 454 v := Value{ 455 originalName: name.Name, 456 value: u64, 457 signed: info&types.IsUnsigned == 0, 458 str: value.String(), 459 } 460 if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 { 461 v.name = strings.TrimSpace(c.Text()) 462 } else { 463 v.name = strings.TrimPrefix(v.originalName, f.trimPrefix) 464 } 465 f.values = append(f.values, v) 466 } 467 } 468 return false 469} 470 471// Helpers 472 473// usize returns the number of bits of the smallest unsigned integer 474// type that will hold n. Used to create the smallest possible slice of 475// integers to use as indexes into the concatenated strings. 476func usize(n int) int { 477 switch { 478 case n < 1<<8: 479 return 8 480 case n < 1<<16: 481 return 16 482 default: 483 // 2^32 is enough constants for anyone. 484 return 32 485 } 486} 487 488// declareIndexAndNameVars declares the index slices and concatenated names 489// strings representing the runs of values. 490func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { 491 var indexes, names []string 492 for i, run := range runs { 493 index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) 494 if len(run) != 1 { 495 indexes = append(indexes, index) 496 } 497 names = append(names, name) 498 } 499 g.Printf("const (\n") 500 for _, name := range names { 501 g.Printf("\t%s\n", name) 502 } 503 g.Printf(")\n\n") 504 505 if len(indexes) > 0 { 506 g.Printf("var (") 507 for _, index := range indexes { 508 g.Printf("\t%s\n", index) 509 } 510 g.Printf(")\n\n") 511 } 512} 513 514// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars 515func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { 516 index, name := g.createIndexAndNameDecl(run, typeName, "") 517 g.Printf("const %s\n", name) 518 g.Printf("var %s\n", index) 519} 520 521// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var". 522func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { 523 b := new(bytes.Buffer) 524 indexes := make([]int, len(run)) 525 for i := range run { 526 b.WriteString(run[i].name) 527 indexes[i] = b.Len() 528 } 529 nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) 530 nameLen := b.Len() 531 b.Reset() 532 fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) 533 for i, v := range indexes { 534 if i > 0 { 535 fmt.Fprintf(b, ", ") 536 } 537 fmt.Fprintf(b, "%d", v) 538 } 539 fmt.Fprintf(b, "}") 540 return b.String(), nameConst 541} 542 543// declareNameVars declares the concatenated names string representing all the values in the runs. 544func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) { 545 g.Printf("const _%s_name%s = \"", typeName, suffix) 546 for _, run := range runs { 547 for i := range run { 548 g.Printf("%s", run[i].name) 549 } 550 } 551 g.Printf("\"\n") 552} 553 554// buildOneRun generates the variables and String method for a single run of contiguous values. 555func (g *Generator) buildOneRun(runs [][]Value, typeName string) { 556 values := runs[0] 557 g.Printf("\n") 558 g.declareIndexAndNameVar(values, typeName) 559 // The generated code is simple enough to write as a Printf format. 560 lessThanZero := "" 561 if values[0].signed { 562 lessThanZero = "i < 0 || " 563 } 564 if values[0].value == 0 { // Signed or unsigned, 0 is still 0. 565 g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) 566 } else { 567 g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) 568 } 569} 570 571// Arguments to format are: 572// [1]: type name 573// [2]: size of index element (8 for uint8 etc.) 574// [3]: less than zero check (for signed types) 575const stringOneRun = `func (i %[1]s) String() string { 576 if %[3]si >= %[1]s(len(_%[1]s_index)-1) { 577 return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" 578 } 579 return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] 580} 581` 582 583// Arguments to format are: 584// [1]: type name 585// [2]: lowest defined value for type, as a string 586// [3]: size of index element (8 for uint8 etc.) 587// [4]: less than zero check (for signed types) 588/* 589 */ 590const stringOneRunWithOffset = `func (i %[1]s) String() string { 591 i -= %[2]s 592 if %[4]si >= %[1]s(len(_%[1]s_index)-1) { 593 return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")" 594 } 595 return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]] 596} 597` 598 599// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. 600// For this pattern, a single Printf format won't do. 601func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { 602 g.Printf("\n") 603 g.declareIndexAndNameVars(runs, typeName) 604 g.Printf("func (i %s) String() string {\n", typeName) 605 g.Printf("\tswitch {\n") 606 for i, values := range runs { 607 if len(values) == 1 { 608 g.Printf("\tcase i == %s:\n", &values[0]) 609 g.Printf("\t\treturn _%s_name_%d\n", typeName, i) 610 continue 611 } 612 g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) 613 if values[0].value != 0 { 614 g.Printf("\t\ti -= %s\n", &values[0]) 615 } 616 g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n", 617 typeName, i, typeName, i, typeName, i) 618 } 619 g.Printf("\tdefault:\n") 620 g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) 621 g.Printf("\t}\n") 622 g.Printf("}\n") 623} 624 625// buildMap handles the case where the space is so sparse a map is a reasonable fallback. 626// It's a rare situation but has simple code. 627func (g *Generator) buildMap(runs [][]Value, typeName string) { 628 g.Printf("\n") 629 g.declareNameVars(runs, typeName, "") 630 g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) 631 n := 0 632 for _, values := range runs { 633 for _, value := range values { 634 g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) 635 n += len(value.name) 636 } 637 } 638 g.Printf("}\n\n") 639 g.Printf(stringMap, typeName) 640} 641 642// Argument to format is the type name. 643const stringMap = `func (i %[1]s) String() string { 644 if str, ok := _%[1]s_map[i]; ok { 645 return str 646 } 647 return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" 648} 649` 650