Home | History | Annotate | Download | only in compile
      1 // Copyright 2016 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 // This file implements TestFormats; a test that verifies
      6 // format strings in the compiler (this directory and all
      7 // subdirectories, recursively).
      8 //
      9 // TestFormats finds potential (Printf, etc.) format strings.
     10 // If they are used in a call, the format verbs are verified
     11 // based on the matching argument type against a precomputed
     12 // table of valid formats. The knownFormats table can be used
     13 // to automatically rewrite format strings with the -u flag.
     14 //
     15 // A new knownFormats table based on the found formats is printed
     16 // when the test is run in verbose mode (-v flag). The table
     17 // needs to be updated whenever a new (type, format) combination
     18 // is found and the format verb is not 'v' or 'T' (as in "%v" or
     19 // "%T").
     20 //
     21 // Run as: go test -run Formats [-u][-v]
     22 //
     23 // Known bugs:
     24 // - indexed format strings ("%[2]s", etc.) are not supported
     25 //   (the test will fail)
     26 // - format strings that are not simple string literals cannot
     27 //   be updated automatically
     28 //   (the test will fail with respective warnings)
     29 // - format strings in _test packages outside the current
     30 //   package are not processed
     31 //   (the test will report those files)
     32 //
     33 package main_test
     34 
     35 import (
     36 	"bytes"
     37 	"flag"
     38 	"fmt"
     39 	"go/ast"
     40 	"go/build"
     41 	"go/constant"
     42 	"go/format"
     43 	"go/importer"
     44 	"go/parser"
     45 	"go/token"
     46 	"go/types"
     47 	"internal/testenv"
     48 	"io/ioutil"
     49 	"log"
     50 	"os"
     51 	"path/filepath"
     52 	"sort"
     53 	"strconv"
     54 	"strings"
     55 	"testing"
     56 	"unicode/utf8"
     57 )
     58 
     59 var update = flag.Bool("u", false, "update format strings")
     60 
     61 // The following variables collect information across all processed files.
     62 var (
     63 	fset          = token.NewFileSet()
     64 	formatStrings = make(map[*ast.BasicLit]bool)      // set of all potential format strings found
     65 	foundFormats  = make(map[string]bool)             // set of all formats found
     66 	callSites     = make(map[*ast.CallExpr]*callSite) // map of all calls
     67 )
     68 
     69 // A File is a corresponding (filename, ast) pair.
     70 type File struct {
     71 	name string
     72 	ast  *ast.File
     73 }
     74 
     75 func TestFormats(t *testing.T) {
     76 	testenv.MustHaveGoBuild(t) // more restrictive than necessary, but that's ok
     77 
     78 	// process all directories
     79 	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
     80 		if info.IsDir() {
     81 			if info.Name() == "testdata" {
     82 				return filepath.SkipDir
     83 			}
     84 
     85 			importPath := filepath.Join("cmd/compile", path)
     86 			if blacklistedPackages[filepath.ToSlash(importPath)] {
     87 				return filepath.SkipDir
     88 			}
     89 
     90 			pkg, err := build.Import(importPath, path, 0)
     91 			if err != nil {
     92 				if _, ok := err.(*build.NoGoError); ok {
     93 					return nil // nothing to do here
     94 				}
     95 				t.Fatal(err)
     96 			}
     97 			collectPkgFormats(t, pkg)
     98 		}
     99 		return nil
    100 	})
    101 
    102 	// test and rewrite formats
    103 	updatedFiles := make(map[string]File) // files that were rewritten
    104 	for _, p := range callSites {
    105 		// test current format literal and determine updated one
    106 		out := formatReplace(p.str, func(index int, in string) string {
    107 			if in == "*" {
    108 				return in // cannot rewrite '*' (as in "%*d")
    109 			}
    110 			// in != '*'
    111 			typ := p.types[index]
    112 			format := typ + " " + in // e.g., "*Node %n"
    113 
    114 			// check if format is known
    115 			out, known := knownFormats[format]
    116 
    117 			// record format if not yet found
    118 			_, found := foundFormats[format]
    119 			if !found {
    120 				foundFormats[format] = true
    121 			}
    122 
    123 			// report an error if the format is unknown and this is the first
    124 			// time we see it; ignore "%v" and "%T" which are always valid
    125 			if !known && !found && in != "%v" && in != "%T" {
    126 				t.Errorf("%s: unknown format %q for %s argument", posString(p.arg), in, typ)
    127 			}
    128 
    129 			if out == "" {
    130 				out = in
    131 			}
    132 			return out
    133 		})
    134 
    135 		// replace existing format literal if it changed
    136 		if out != p.str {
    137 			// we cannot replace the argument if it's not a string literal for now
    138 			// (e.g., it may be "foo" + "bar")
    139 			lit, ok := p.arg.(*ast.BasicLit)
    140 			if !ok {
    141 				delete(callSites, p.call) // treat as if we hadn't found this site
    142 				continue
    143 			}
    144 
    145 			if testing.Verbose() {
    146 				fmt.Printf("%s:\n\t- %q\n\t+ %q\n", posString(p.arg), p.str, out)
    147 			}
    148 
    149 			// find argument index of format argument
    150 			index := -1
    151 			for i, arg := range p.call.Args {
    152 				if p.arg == arg {
    153 					index = i
    154 					break
    155 				}
    156 			}
    157 			if index < 0 {
    158 				// we may have processed the same call site twice,
    159 				// but that shouldn't happen
    160 				panic("internal error: matching argument not found")
    161 			}
    162 
    163 			// replace literal
    164 			new := *lit                    // make a copy
    165 			new.Value = strconv.Quote(out) // this may introduce "-quotes where there were `-quotes
    166 			p.call.Args[index] = &new
    167 			updatedFiles[p.file.name] = p.file
    168 		}
    169 	}
    170 
    171 	// write dirty files back
    172 	var filesUpdated bool
    173 	if len(updatedFiles) > 0 && *update {
    174 		for _, file := range updatedFiles {
    175 			var buf bytes.Buffer
    176 			if err := format.Node(&buf, fset, file.ast); err != nil {
    177 				t.Errorf("WARNING: formatting %s failed: %v", file.name, err)
    178 				continue
    179 			}
    180 			if err := ioutil.WriteFile(file.name, buf.Bytes(), 0x666); err != nil {
    181 				t.Errorf("WARNING: writing %s failed: %v", file.name, err)
    182 				continue
    183 			}
    184 			fmt.Printf("updated %s\n", file.name)
    185 			filesUpdated = true
    186 		}
    187 	}
    188 
    189 	// report all function names containing a format string
    190 	if len(callSites) > 0 && testing.Verbose() {
    191 		set := make(map[string]bool)
    192 		for _, p := range callSites {
    193 			set[nodeString(p.call.Fun)] = true
    194 		}
    195 		var list []string
    196 		for s := range set {
    197 			list = append(list, s)
    198 		}
    199 		fmt.Println("\nFunctions")
    200 		printList(list)
    201 	}
    202 
    203 	// report all formats found
    204 	if len(foundFormats) > 0 && testing.Verbose() {
    205 		var list []string
    206 		for s := range foundFormats {
    207 			list = append(list, fmt.Sprintf("%q: \"\",", s))
    208 		}
    209 		fmt.Println("\nvar knownFormats = map[string]string{")
    210 		printList(list)
    211 		fmt.Println("}")
    212 	}
    213 
    214 	// check that knownFormats is up to date
    215 	if !testing.Verbose() && !*update {
    216 		var mismatch bool
    217 		for s := range foundFormats {
    218 			if _, ok := knownFormats[s]; !ok {
    219 				mismatch = true
    220 				break
    221 			}
    222 		}
    223 		if !mismatch {
    224 			for s := range knownFormats {
    225 				if _, ok := foundFormats[s]; !ok {
    226 					mismatch = true
    227 					break
    228 				}
    229 			}
    230 		}
    231 		if mismatch {
    232 			t.Errorf("knownFormats is out of date; please run with -v to regenerate")
    233 		}
    234 	}
    235 
    236 	// all format strings of calls must be in the formatStrings set (self-verification)
    237 	for _, p := range callSites {
    238 		if lit, ok := p.arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
    239 			if formatStrings[lit] {
    240 				// ok
    241 				delete(formatStrings, lit)
    242 			} else {
    243 				// this should never happen
    244 				panic(fmt.Sprintf("internal error: format string not found (%s)", posString(lit)))
    245 			}
    246 		}
    247 	}
    248 
    249 	// if we have any strings left, we may need to update them manually
    250 	if len(formatStrings) > 0 && filesUpdated {
    251 		var list []string
    252 		for lit := range formatStrings {
    253 			list = append(list, fmt.Sprintf("%s: %s", posString(lit), nodeString(lit)))
    254 		}
    255 		fmt.Println("\nWARNING: Potentially missed format strings")
    256 		printList(list)
    257 		t.Fail()
    258 	}
    259 
    260 	fmt.Println()
    261 }
    262 
    263 // A callSite describes a function call that appears to contain
    264 // a format string.
    265 type callSite struct {
    266 	file  File
    267 	call  *ast.CallExpr // call containing the format string
    268 	arg   ast.Expr      // format argument (string literal or constant)
    269 	str   string        // unquoted format string
    270 	types []string      // argument types
    271 }
    272 
    273 func collectPkgFormats(t *testing.T, pkg *build.Package) {
    274 	// collect all files
    275 	var filenames []string
    276 	filenames = append(filenames, pkg.GoFiles...)
    277 	filenames = append(filenames, pkg.CgoFiles...)
    278 	filenames = append(filenames, pkg.TestGoFiles...)
    279 
    280 	// TODO(gri) verify _test files outside package
    281 	for _, name := range pkg.XTestGoFiles {
    282 		// don't process this test itself
    283 		if name != "fmt_test.go" && testing.Verbose() {
    284 			fmt.Printf("WARNING: %s not processed\n", filepath.Join(pkg.Dir, name))
    285 		}
    286 	}
    287 
    288 	// make filenames relative to .
    289 	for i, name := range filenames {
    290 		filenames[i] = filepath.Join(pkg.Dir, name)
    291 	}
    292 
    293 	// parse all files
    294 	files := make([]*ast.File, len(filenames))
    295 	for i, filename := range filenames {
    296 		f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
    297 		if err != nil {
    298 			t.Fatal(err)
    299 		}
    300 		files[i] = f
    301 	}
    302 
    303 	// typecheck package
    304 	conf := types.Config{Importer: importer.Default()}
    305 	etypes := make(map[ast.Expr]types.TypeAndValue)
    306 	if _, err := conf.Check(pkg.ImportPath, fset, files, &types.Info{Types: etypes}); err != nil {
    307 		t.Fatal(err)
    308 	}
    309 
    310 	// collect all potential format strings (for extra verification later)
    311 	for _, file := range files {
    312 		ast.Inspect(file, func(n ast.Node) bool {
    313 			if s, ok := stringLit(n); ok && isFormat(s) {
    314 				formatStrings[n.(*ast.BasicLit)] = true
    315 			}
    316 			return true
    317 		})
    318 	}
    319 
    320 	// collect all formats/arguments of calls with format strings
    321 	for index, file := range files {
    322 		ast.Inspect(file, func(n ast.Node) bool {
    323 			if call, ok := n.(*ast.CallExpr); ok {
    324 				// ignore blacklisted functions
    325 				if blacklistedFunctions[nodeString(call.Fun)] {
    326 					return true
    327 				}
    328 				// look for an arguments that might be a format string
    329 				for i, arg := range call.Args {
    330 					if s, ok := stringVal(etypes[arg]); ok && isFormat(s) {
    331 						// make sure we have enough arguments
    332 						n := numFormatArgs(s)
    333 						if i+1+n > len(call.Args) {
    334 							t.Errorf("%s: not enough format args (blacklist %s?)", posString(call), nodeString(call.Fun))
    335 							break // ignore this call
    336 						}
    337 						// assume last n arguments are to be formatted;
    338 						// determine their types
    339 						argTypes := make([]string, n)
    340 						for i, arg := range call.Args[len(call.Args)-n:] {
    341 							if tv, ok := etypes[arg]; ok {
    342 								argTypes[i] = typeString(tv.Type)
    343 							}
    344 						}
    345 						// collect call site
    346 						if callSites[call] != nil {
    347 							panic("internal error: file processed twice?")
    348 						}
    349 						callSites[call] = &callSite{
    350 							file:  File{filenames[index], file},
    351 							call:  call,
    352 							arg:   arg,
    353 							str:   s,
    354 							types: argTypes,
    355 						}
    356 						break // at most one format per argument list
    357 					}
    358 				}
    359 			}
    360 			return true
    361 		})
    362 	}
    363 }
    364 
    365 // printList prints list in sorted order.
    366 func printList(list []string) {
    367 	sort.Strings(list)
    368 	for _, s := range list {
    369 		fmt.Println("\t", s)
    370 	}
    371 }
    372 
    373 // posString returns a string representation of n's position
    374 // in the form filename:line:col: .
    375 func posString(n ast.Node) string {
    376 	if n == nil {
    377 		return ""
    378 	}
    379 	return fset.Position(n.Pos()).String()
    380 }
    381 
    382 // nodeString returns a string representation of n.
    383 func nodeString(n ast.Node) string {
    384 	var buf bytes.Buffer
    385 	if err := format.Node(&buf, fset, n); err != nil {
    386 		log.Fatal(err) // should always succeed
    387 	}
    388 	return buf.String()
    389 }
    390 
    391 // typeString returns a string representation of n.
    392 func typeString(typ types.Type) string {
    393 	return filepath.ToSlash(typ.String())
    394 }
    395 
    396 // stringLit returns the unquoted string value and true if
    397 // n represents a string literal; otherwise it returns ""
    398 // and false.
    399 func stringLit(n ast.Node) (string, bool) {
    400 	if lit, ok := n.(*ast.BasicLit); ok && lit.Kind == token.STRING {
    401 		s, err := strconv.Unquote(lit.Value)
    402 		if err != nil {
    403 			log.Fatal(err) // should not happen with correct ASTs
    404 		}
    405 		return s, true
    406 	}
    407 	return "", false
    408 }
    409 
    410 // stringVal returns the (unquoted) string value and true if
    411 // tv is a string constant; otherwise it returns "" and false.
    412 func stringVal(tv types.TypeAndValue) (string, bool) {
    413 	if tv.IsValue() && tv.Value != nil && tv.Value.Kind() == constant.String {
    414 		return constant.StringVal(tv.Value), true
    415 	}
    416 	return "", false
    417 }
    418 
    419 // formatIter iterates through the string s in increasing
    420 // index order and calls f for each format specifier '%..v'.
    421 // The arguments for f describe the specifier's index range.
    422 // If a format specifier contains a  "*", f is called with
    423 // the index range for "*" alone, before being called for
    424 // the entire specifier. The result of f is the index of
    425 // the rune at which iteration continues.
    426 func formatIter(s string, f func(i, j int) int) {
    427 	i := 0     // index after current rune
    428 	var r rune // current rune
    429 
    430 	next := func() {
    431 		r1, w := utf8.DecodeRuneInString(s[i:])
    432 		if w == 0 {
    433 			r1 = -1 // signal end-of-string
    434 		}
    435 		r = r1
    436 		i += w
    437 	}
    438 
    439 	flags := func() {
    440 		for r == ' ' || r == '#' || r == '+' || r == '-' || r == '0' {
    441 			next()
    442 		}
    443 	}
    444 
    445 	index := func() {
    446 		if r == '[' {
    447 			log.Fatalf("cannot handle indexed arguments: %s", s)
    448 		}
    449 	}
    450 
    451 	digits := func() {
    452 		index()
    453 		if r == '*' {
    454 			i = f(i-1, i)
    455 			next()
    456 			return
    457 		}
    458 		for '0' <= r && r <= '9' {
    459 			next()
    460 		}
    461 	}
    462 
    463 	for next(); r >= 0; next() {
    464 		if r == '%' {
    465 			i0 := i
    466 			next()
    467 			flags()
    468 			digits()
    469 			if r == '.' {
    470 				next()
    471 				digits()
    472 			}
    473 			index()
    474 			// accept any letter (a-z, A-Z) as format verb;
    475 			// ignore anything else
    476 			if 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' {
    477 				i = f(i0-1, i)
    478 			}
    479 		}
    480 	}
    481 }
    482 
    483 // isFormat reports whether s contains format specifiers.
    484 func isFormat(s string) (yes bool) {
    485 	formatIter(s, func(i, j int) int {
    486 		yes = true
    487 		return len(s) // stop iteration
    488 	})
    489 	return
    490 }
    491 
    492 // oneFormat reports whether s is exactly one format specifier.
    493 func oneFormat(s string) (yes bool) {
    494 	formatIter(s, func(i, j int) int {
    495 		yes = i == 0 && j == len(s)
    496 		return j
    497 	})
    498 	return
    499 }
    500 
    501 // numFormatArgs returns the number of format specifiers in s.
    502 func numFormatArgs(s string) int {
    503 	count := 0
    504 	formatIter(s, func(i, j int) int {
    505 		count++
    506 		return j
    507 	})
    508 	return count
    509 }
    510 
    511 // formatReplace replaces the i'th format specifier s in the incoming
    512 // string in with the result of f(i, s) and returns the new string.
    513 func formatReplace(in string, f func(i int, s string) string) string {
    514 	var buf []byte
    515 	i0 := 0
    516 	index := 0
    517 	formatIter(in, func(i, j int) int {
    518 		if sub := in[i:j]; sub != "*" { // ignore calls for "*" width/length specifiers
    519 			buf = append(buf, in[i0:i]...)
    520 			buf = append(buf, f(index, sub)...)
    521 			i0 = j
    522 		}
    523 		index++
    524 		return j
    525 	})
    526 	return string(append(buf, in[i0:]...))
    527 }
    528 
    529 // blacklistedPackages is the set of packages which can
    530 // be ignored.
    531 var blacklistedPackages = map[string]bool{}
    532 
    533 // blacklistedFunctions is the set of functions which may have
    534 // format-like arguments but which don't do any formatting and
    535 // thus may be ignored.
    536 var blacklistedFunctions = map[string]bool{}
    537 
    538 func init() {
    539 	// verify that knownFormats entries are correctly formatted
    540 	for key, val := range knownFormats {
    541 		// key must be "typename format", and format starts with a '%'
    542 		// (formats containing '*' alone are not collected in this table)
    543 		i := strings.Index(key, "%")
    544 		if i < 0 || !oneFormat(key[i:]) {
    545 			log.Fatalf("incorrect knownFormats key: %q", key)
    546 		}
    547 		// val must be "format" or ""
    548 		if val != "" && !oneFormat(val) {
    549 			log.Fatalf("incorrect knownFormats value: %q (key = %q)", val, key)
    550 		}
    551 	}
    552 }
    553 
    554 // knownFormats entries are of the form "typename format" -> "newformat".
    555 // An absent entry means that the format is not recognized as valid.
    556 // An empty new format means that the format should remain unchanged.
    557 // To print out a new table, run: go test -run Formats -v.
    558 var knownFormats = map[string]string{
    559 	"*bytes.Buffer %s":                                "",
    560 	"*cmd/compile/internal/gc.Field %p":               "",
    561 	"*cmd/compile/internal/gc.Field %v":               "",
    562 	"*cmd/compile/internal/gc.Mpflt %v":               "",
    563 	"*cmd/compile/internal/gc.Mpint %v":               "",
    564 	"*cmd/compile/internal/gc.Node %#v":               "",
    565 	"*cmd/compile/internal/gc.Node %+S":               "",
    566 	"*cmd/compile/internal/gc.Node %+v":               "",
    567 	"*cmd/compile/internal/gc.Node %0j":               "",
    568 	"*cmd/compile/internal/gc.Node %L":                "",
    569 	"*cmd/compile/internal/gc.Node %S":                "",
    570 	"*cmd/compile/internal/gc.Node %j":                "",
    571 	"*cmd/compile/internal/gc.Node %p":                "",
    572 	"*cmd/compile/internal/gc.Node %v":                "",
    573 	"*cmd/compile/internal/gc.Sym %+v":                "",
    574 	"*cmd/compile/internal/gc.Sym %-v":                "",
    575 	"*cmd/compile/internal/gc.Sym %0S":                "",
    576 	"*cmd/compile/internal/gc.Sym %S":                 "",
    577 	"*cmd/compile/internal/gc.Sym %p":                 "",
    578 	"*cmd/compile/internal/gc.Sym %v":                 "",
    579 	"*cmd/compile/internal/gc.Type %#v":               "",
    580 	"*cmd/compile/internal/gc.Type %+v":               "",
    581 	"*cmd/compile/internal/gc.Type %-S":               "",
    582 	"*cmd/compile/internal/gc.Type %0S":               "",
    583 	"*cmd/compile/internal/gc.Type %L":                "",
    584 	"*cmd/compile/internal/gc.Type %S":                "",
    585 	"*cmd/compile/internal/gc.Type %p":                "",
    586 	"*cmd/compile/internal/gc.Type %v":                "",
    587 	"*cmd/compile/internal/ssa.Block %s":              "",
    588 	"*cmd/compile/internal/ssa.Block %v":              "",
    589 	"*cmd/compile/internal/ssa.Func %s":               "",
    590 	"*cmd/compile/internal/ssa.SparseTreeNode %v":     "",
    591 	"*cmd/compile/internal/ssa.Value %s":              "",
    592 	"*cmd/compile/internal/ssa.Value %v":              "",
    593 	"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
    594 	"*cmd/internal/obj.Addr %v":                       "",
    595 	"*cmd/internal/obj.Prog %p":                       "",
    596 	"*cmd/internal/obj.Prog %s":                       "",
    597 	"*cmd/internal/obj.Prog %v":                       "",
    598 	"*math/big.Int %#x":                               "",
    599 	"[16]byte %x":                                     "",
    600 	"[]*cmd/compile/internal/gc.Node %v":              "",
    601 	"[]*cmd/compile/internal/gc.Sig %#v":              "",
    602 	"[]*cmd/compile/internal/ssa.Value %v":            "",
    603 	"[]byte %s":                                       "",
    604 	"[]byte %x":                                       "",
    605 	"[]cmd/compile/internal/ssa.Edge %v":              "",
    606 	"[]cmd/compile/internal/ssa.ID %v":                "",
    607 	"[]string %v":                                     "",
    608 	"bool %v":                                         "",
    609 	"byte %02x":                                       "",
    610 	"byte %08b":                                       "",
    611 	"byte %c":                                         "",
    612 	"cmd/compile/internal/arm.shift %d":               "",
    613 	"cmd/compile/internal/gc.Class %d":                "",
    614 	"cmd/compile/internal/gc.Ctype %d":                "",
    615 	"cmd/compile/internal/gc.Ctype %v":                "",
    616 	"cmd/compile/internal/gc.EType %d":                "",
    617 	"cmd/compile/internal/gc.EType %s":                "",
    618 	"cmd/compile/internal/gc.EType %v":                "",
    619 	"cmd/compile/internal/gc.Level %d":                "",
    620 	"cmd/compile/internal/gc.Level %v":                "",
    621 	"cmd/compile/internal/gc.Node %#v":                "",
    622 	"cmd/compile/internal/gc.Nodes %#v":               "",
    623 	"cmd/compile/internal/gc.Nodes %+v":               "",
    624 	"cmd/compile/internal/gc.Nodes %.v":               "",
    625 	"cmd/compile/internal/gc.Nodes %v":                "",
    626 	"cmd/compile/internal/gc.Op %#v":                  "",
    627 	"cmd/compile/internal/gc.Op %v":                   "",
    628 	"cmd/compile/internal/gc.Val %#v":                 "",
    629 	"cmd/compile/internal/gc.Val %T":                  "",
    630 	"cmd/compile/internal/gc.Val %v":                  "",
    631 	"cmd/compile/internal/gc.initKind %d":             "",
    632 	"cmd/compile/internal/ssa.BranchPrediction %d":    "",
    633 	"cmd/compile/internal/ssa.Edge %v":                "",
    634 	"cmd/compile/internal/ssa.GCNode %v":              "",
    635 	"cmd/compile/internal/ssa.ID %d":                  "",
    636 	"cmd/compile/internal/ssa.LocalSlot %v":           "",
    637 	"cmd/compile/internal/ssa.Location %v":            "",
    638 	"cmd/compile/internal/ssa.Op %s":                  "",
    639 	"cmd/compile/internal/ssa.Op %v":                  "",
    640 	"cmd/compile/internal/ssa.SizeAndAlign %s":        "",
    641 	"cmd/compile/internal/ssa.Type %s":                "",
    642 	"cmd/compile/internal/ssa.Type %v":                "",
    643 	"cmd/compile/internal/ssa.ValAndOff %s":           "",
    644 	"cmd/compile/internal/ssa.markKind %d":            "",
    645 	"cmd/compile/internal/ssa.rbrank %d":              "",
    646 	"cmd/compile/internal/ssa.regMask %d":             "",
    647 	"cmd/compile/internal/ssa.register %d":            "",
    648 	"cmd/compile/internal/syntax.Expr %#v":            "",
    649 	"cmd/compile/internal/syntax.Expr %s":             "",
    650 	"cmd/compile/internal/syntax.Node %T":             "",
    651 	"cmd/compile/internal/syntax.Operator %d":         "",
    652 	"cmd/compile/internal/syntax.Operator %s":         "",
    653 	"cmd/compile/internal/syntax.token %d":            "",
    654 	"cmd/compile/internal/syntax.token %q":            "",
    655 	"cmd/compile/internal/syntax.token %s":            "",
    656 	"cmd/internal/obj.As %v":                          "",
    657 	"error %v":                                        "",
    658 	"float64 %.2f":                                    "",
    659 	"float64 %.3f":                                    "",
    660 	"float64 %.6g":                                    "",
    661 	"float64 %g":                                      "",
    662 	"fmt.Stringer %T":                                 "",
    663 	"int %-12d":                                       "",
    664 	"int %-6d":                                        "",
    665 	"int %-8o":                                        "",
    666 	"int %5d":                                         "",
    667 	"int %6d":                                         "",
    668 	"int %c":                                          "",
    669 	"int %d":                                          "",
    670 	"int %v":                                          "",
    671 	"int %x":                                          "",
    672 	"int16 %d":                                        "",
    673 	"int16 %x":                                        "",
    674 	"int32 %d":                                        "",
    675 	"int32 %v":                                        "",
    676 	"int32 %x":                                        "",
    677 	"int64 %+d":                                       "",
    678 	"int64 %-10d":                                     "",
    679 	"int64 %X":                                        "",
    680 	"int64 %d":                                        "",
    681 	"int64 %v":                                        "",
    682 	"int64 %x":                                        "",
    683 	"int8 %d":                                         "",
    684 	"int8 %x":                                         "",
    685 	"interface{} %#v":                                 "",
    686 	"interface{} %T":                                  "",
    687 	"interface{} %q":                                  "",
    688 	"interface{} %s":                                  "",
    689 	"interface{} %v":                                  "",
    690 	"map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
    691 	"reflect.Type %s":  "",
    692 	"rune %#U":         "",
    693 	"rune %c":          "",
    694 	"string %-16s":     "",
    695 	"string %.*s":      "",
    696 	"string %q":        "",
    697 	"string %s":        "",
    698 	"string %v":        "",
    699 	"time.Duration %d": "",
    700 	"time.Duration %v": "",
    701 	"uint %04x":        "",
    702 	"uint %d":          "",
    703 	"uint16 %d":        "",
    704 	"uint16 %v":        "",
    705 	"uint16 %x":        "",
    706 	"uint32 %08x":      "",
    707 	"uint32 %d":        "",
    708 	"uint32 %x":        "",
    709 	"uint64 %016x":     "",
    710 	"uint64 %08x":      "",
    711 	"uint64 %d":        "",
    712 	"uint64 %x":        "",
    713 	"uint8 %d":         "",
    714 	"uint8 %x":         "",
    715 	"uintptr %d":       "",
    716 }
    717