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