GoPLS Viewer

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