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