]> Cypherpunks.ru repositories - gostls13.git/blob - test/run.go
[dev.typeparams] all: merge master (785a8f6) into dev.typeparams
[gostls13.git] / test / run.go
1 // skip
2
3 // Copyright 2012 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6
7 // Run runs tests in the test directory.
8 package main
9
10 import (
11         "bytes"
12         "errors"
13         "flag"
14         "fmt"
15         "go/build"
16         "hash/fnv"
17         "io"
18         "io/fs"
19         "io/ioutil"
20         "log"
21         "os"
22         "os/exec"
23         "path"
24         "path/filepath"
25         "regexp"
26         "runtime"
27         "sort"
28         "strconv"
29         "strings"
30         "time"
31         "unicode"
32 )
33
34 var (
35         verbose        = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
36         keep           = flag.Bool("k", false, "keep. keep temporary directory.")
37         numParallel    = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
38         summary        = flag.Bool("summary", false, "show summary of results")
39         allCodegen     = flag.Bool("all_codegen", defaultAllCodeGen(), "run all goos/goarch for codegen")
40         showSkips      = flag.Bool("show_skips", false, "show skipped tests")
41         runSkips       = flag.Bool("run_skips", false, "run skipped tests (ignore skip and build tags)")
42         linkshared     = flag.Bool("linkshared", false, "")
43         updateErrors   = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
44         runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
45         generics       = flag.String("G", "0,3", "a comma-separated list of -G compiler flags to test with")
46         force          = flag.Bool("f", false, "run expected-failure generics tests rather than skipping them")
47
48         shard  = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.")
49         shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.")
50 )
51
52 // defaultAllCodeGen returns the default value of the -all_codegen
53 // flag. By default, we prefer to be fast (returning false), except on
54 // the linux-amd64 builder that's already very fast, so we get more
55 // test coverage on trybots. See https://golang.org/issue/34297.
56 func defaultAllCodeGen() bool {
57         return os.Getenv("GO_BUILDER_NAME") == "linux-amd64"
58 }
59
60 var (
61         goos, goarch string
62         cgoEnabled   bool
63
64         // dirs are the directories to look for *.go files in.
65         // TODO(bradfitz): just use all directories?
66         dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam"}
67
68         // ratec controls the max number of tests running at a time.
69         ratec chan bool
70
71         // toRun is the channel of tests to run.
72         // It is nil until the first test is started.
73         toRun chan *test
74
75         // rungatec controls the max number of runoutput tests
76         // executed in parallel as they can each consume a lot of memory.
77         rungatec chan bool
78 )
79
80 // maxTests is an upper bound on the total number of tests.
81 // It is used as a channel buffer size to make sure sends don't block.
82 const maxTests = 5000
83
84 func main() {
85         flag.Parse()
86
87         var glevels []int
88         for _, s := range strings.Split(*generics, ",") {
89                 glevel, err := strconv.Atoi(s)
90                 if err != nil {
91                         log.Fatalf("invalid -G flag: %v", err)
92                 }
93                 glevels = append(glevels, glevel)
94         }
95
96         goos = getenv("GOOS", runtime.GOOS)
97         goarch = getenv("GOARCH", runtime.GOARCH)
98         cgoEnv, err := exec.Command(goTool(), "env", "CGO_ENABLED").Output()
99         if err == nil {
100                 cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(cgoEnv)))
101         }
102
103         findExecCmd()
104
105         // Disable parallelism if printing or if using a simulator.
106         if *verbose || len(findExecCmd()) > 0 {
107                 *numParallel = 1
108                 *runoutputLimit = 1
109         }
110
111         ratec = make(chan bool, *numParallel)
112         rungatec = make(chan bool, *runoutputLimit)
113
114         var tests []*test
115         if flag.NArg() > 0 {
116                 for _, arg := range flag.Args() {
117                         if arg == "-" || arg == "--" {
118                                 // Permit running:
119                                 // $ go run run.go - env.go
120                                 // $ go run run.go -- env.go
121                                 // $ go run run.go - ./fixedbugs
122                                 // $ go run run.go -- ./fixedbugs
123                                 continue
124                         }
125                         if fi, err := os.Stat(arg); err == nil && fi.IsDir() {
126                                 for _, baseGoFile := range goFiles(arg) {
127                                         tests = append(tests, startTests(arg, baseGoFile, glevels)...)
128                                 }
129                         } else if strings.HasSuffix(arg, ".go") {
130                                 dir, file := filepath.Split(arg)
131                                 tests = append(tests, startTests(dir, file, glevels)...)
132                         } else {
133                                 log.Fatalf("can't yet deal with non-directory and non-go file %q", arg)
134                         }
135                 }
136         } else {
137                 for _, dir := range dirs {
138                         for _, baseGoFile := range goFiles(dir) {
139                                 tests = append(tests, startTests(dir, baseGoFile, glevels)...)
140                         }
141                 }
142         }
143
144         failed := false
145         resCount := map[string]int{}
146         for _, test := range tests {
147                 <-test.donec
148                 status := "ok  "
149                 errStr := ""
150                 if e, isSkip := test.err.(skipError); isSkip {
151                         test.err = nil
152                         errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + string(e)
153                         status = "FAIL"
154                 }
155                 if test.err != nil {
156                         status = "FAIL"
157                         errStr = test.err.Error()
158                 }
159                 if status == "FAIL" {
160                         failed = true
161                 }
162                 resCount[status]++
163                 dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
164                 if status == "FAIL" {
165                         fmt.Printf("# go run run.go -G=%v %s\n%s\nFAIL\t%s\t%s\n",
166                                 test.glevel,
167                                 path.Join(test.dir, test.gofile),
168                                 errStr, test.goFileName(), dt)
169                         continue
170                 }
171                 if !*verbose {
172                         continue
173                 }
174                 fmt.Printf("%s\t%s\t%s\n", status, test.goFileName(), dt)
175         }
176
177         if *summary {
178                 for k, v := range resCount {
179                         fmt.Printf("%5d %s\n", v, k)
180                 }
181         }
182
183         if failed {
184                 os.Exit(1)
185         }
186 }
187
188 // goTool reports the path of the go tool to use to run the tests.
189 // If possible, use the same Go used to run run.go, otherwise
190 // fallback to the go version found in the PATH.
191 func goTool() string {
192         var exeSuffix string
193         if runtime.GOOS == "windows" {
194                 exeSuffix = ".exe"
195         }
196         path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
197         if _, err := os.Stat(path); err == nil {
198                 return path
199         }
200         // Just run "go" from PATH
201         return "go"
202 }
203
204 func shardMatch(name string) bool {
205         if *shards == 0 {
206                 return true
207         }
208         h := fnv.New32()
209         io.WriteString(h, name)
210         return int(h.Sum32()%uint32(*shards)) == *shard
211 }
212
213 func goFiles(dir string) []string {
214         f, err := os.Open(dir)
215         if err != nil {
216                 log.Fatal(err)
217         }
218         dirnames, err := f.Readdirnames(-1)
219         f.Close()
220         if err != nil {
221                 log.Fatal(err)
222         }
223         names := []string{}
224         for _, name := range dirnames {
225                 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) {
226                         names = append(names, name)
227                 }
228         }
229         sort.Strings(names)
230         return names
231 }
232
233 type runCmd func(...string) ([]byte, error)
234
235 func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) {
236         cmd := []string{goTool(), "tool", "compile", "-e"}
237         cmd = append(cmd, flags...)
238         if *linkshared {
239                 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
240         }
241         cmd = append(cmd, longname)
242         return runcmd(cmd...)
243 }
244
245 func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) {
246         cmd := []string{goTool(), "tool", "compile", "-e"}
247         if localImports {
248                 // Set relative path for local imports and import search path to current dir.
249                 cmd = append(cmd, "-D", ".", "-I", ".")
250         }
251         cmd = append(cmd, flags...)
252         if *linkshared {
253                 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
254         }
255         for _, name := range names {
256                 cmd = append(cmd, filepath.Join(dir, name))
257         }
258         return runcmd(cmd...)
259 }
260
261 func linkFile(runcmd runCmd, goname string, ldflags []string) (err error) {
262         pfile := strings.Replace(goname, ".go", ".o", -1)
263         cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."}
264         if *linkshared {
265                 cmd = append(cmd, "-linkshared", "-installsuffix=dynlink")
266         }
267         if ldflags != nil {
268                 cmd = append(cmd, ldflags...)
269         }
270         cmd = append(cmd, pfile)
271         _, err = runcmd(cmd...)
272         return
273 }
274
275 // skipError describes why a test was skipped.
276 type skipError string
277
278 func (s skipError) Error() string { return string(s) }
279
280 // test holds the state of a test.
281 type test struct {
282         dir, gofile string
283         donec       chan bool // closed when done
284         dt          time.Duration
285         glevel      int // what -G level this test should use
286
287         src string
288
289         tempDir string
290         err     error
291 }
292
293 func startTests(dir, gofile string, glevels []int) []*test {
294         tests := make([]*test, len(glevels))
295         for i, glevel := range glevels {
296                 t := &test{
297                         dir:    dir,
298                         gofile: gofile,
299                         glevel: glevel,
300                         donec:  make(chan bool, 1),
301                 }
302                 if toRun == nil {
303                         toRun = make(chan *test, maxTests)
304                         go runTests()
305                 }
306                 select {
307                 case toRun <- t:
308                 default:
309                         panic("toRun buffer size (maxTests) is too small")
310                 }
311                 tests[i] = t
312         }
313         return tests
314 }
315
316 // runTests runs tests in parallel, but respecting the order they
317 // were enqueued on the toRun channel.
318 func runTests() {
319         for {
320                 ratec <- true
321                 t := <-toRun
322                 go func() {
323                         t.run()
324                         <-ratec
325                 }()
326         }
327 }
328
329 var cwd, _ = os.Getwd()
330
331 func (t *test) goFileName() string {
332         return filepath.Join(t.dir, t.gofile)
333 }
334
335 func (t *test) goDirName() string {
336         return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1))
337 }
338
339 func goDirFiles(longdir string) (filter []os.FileInfo, err error) {
340         files, dirErr := ioutil.ReadDir(longdir)
341         if dirErr != nil {
342                 return nil, dirErr
343         }
344         for _, gofile := range files {
345                 if filepath.Ext(gofile.Name()) == ".go" {
346                         filter = append(filter, gofile)
347                 }
348         }
349         return
350 }
351
352 var packageRE = regexp.MustCompile(`(?m)^package ([\p{Lu}\p{Ll}\w]+)`)
353
354 func getPackageNameFromSource(fn string) (string, error) {
355         data, err := ioutil.ReadFile(fn)
356         if err != nil {
357                 return "", err
358         }
359         pkgname := packageRE.FindStringSubmatch(string(data))
360         if pkgname == nil {
361                 return "", fmt.Errorf("cannot find package name in %s", fn)
362         }
363         return pkgname[1], nil
364 }
365
366 // If singlefilepkgs is set, each file is considered a separate package
367 // even if the package names are the same.
368 func goDirPackages(longdir string, singlefilepkgs bool) ([][]string, error) {
369         files, err := goDirFiles(longdir)
370         if err != nil {
371                 return nil, err
372         }
373         var pkgs [][]string
374         m := make(map[string]int)
375         for _, file := range files {
376                 name := file.Name()
377                 pkgname, err := getPackageNameFromSource(filepath.Join(longdir, name))
378                 if err != nil {
379                         log.Fatal(err)
380                 }
381                 i, ok := m[pkgname]
382                 if singlefilepkgs || !ok {
383                         i = len(pkgs)
384                         pkgs = append(pkgs, nil)
385                         m[pkgname] = i
386                 }
387                 pkgs[i] = append(pkgs[i], name)
388         }
389         return pkgs, nil
390 }
391
392 type context struct {
393         GOOS       string
394         GOARCH     string
395         cgoEnabled bool
396         noOptEnv   bool
397 }
398
399 // shouldTest looks for build tags in a source file and returns
400 // whether the file should be used according to the tags.
401 func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
402         if *runSkips {
403                 return true, ""
404         }
405         for _, line := range strings.Split(src, "\n") {
406                 line = strings.TrimSpace(line)
407                 if strings.HasPrefix(line, "//") {
408                         line = line[2:]
409                 } else {
410                         continue
411                 }
412                 line = strings.TrimSpace(line)
413                 if len(line) == 0 || line[0] != '+' {
414                         continue
415                 }
416                 gcFlags := os.Getenv("GO_GCFLAGS")
417                 ctxt := &context{
418                         GOOS:       goos,
419                         GOARCH:     goarch,
420                         cgoEnabled: cgoEnabled,
421                         noOptEnv:   strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"),
422                 }
423
424                 words := strings.Fields(line)
425                 if words[0] == "+build" {
426                         ok := false
427                         for _, word := range words[1:] {
428                                 if ctxt.match(word) {
429                                         ok = true
430                                         break
431                                 }
432                         }
433                         if !ok {
434                                 // no matching tag found.
435                                 return false, line
436                         }
437                 }
438         }
439         // no build tags
440         return true, ""
441 }
442
443 func (ctxt *context) match(name string) bool {
444         if name == "" {
445                 return false
446         }
447         if i := strings.Index(name, ","); i >= 0 {
448                 // comma-separated list
449                 return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
450         }
451         if strings.HasPrefix(name, "!!") { // bad syntax, reject always
452                 return false
453         }
454         if strings.HasPrefix(name, "!") { // negation
455                 return len(name) > 1 && !ctxt.match(name[1:])
456         }
457
458         // Tags must be letters, digits, underscores or dots.
459         // Unlike in Go identifiers, all digits are fine (e.g., "386").
460         for _, c := range name {
461                 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
462                         return false
463                 }
464         }
465
466         if strings.HasPrefix(name, "goexperiment.") {
467                 for _, tag := range build.Default.ToolTags {
468                         if tag == name {
469                                 return true
470                         }
471                 }
472                 return false
473         }
474
475         if name == "cgo" && ctxt.cgoEnabled {
476                 return true
477         }
478
479         if name == ctxt.GOOS || name == ctxt.GOARCH || name == "gc" {
480                 return true
481         }
482
483         if ctxt.noOptEnv && name == "gcflags_noopt" {
484                 return true
485         }
486
487         if name == "test_run" {
488                 return true
489         }
490
491         return false
492 }
493
494 func init() { checkShouldTest() }
495
496 // goGcflags returns the -gcflags argument to use with go build / go run.
497 // This must match the flags used for building the standard library,
498 // or else the commands will rebuild any needed packages (like runtime)
499 // over and over.
500 func (t *test) goGcflags() string {
501         flags := os.Getenv("GO_GCFLAGS")
502         if t.glevel != 0 {
503                 flags = fmt.Sprintf("%s -G=%v", flags, t.glevel)
504         }
505         return "-gcflags=all=" + flags
506 }
507
508 func (t *test) goGcflagsIsEmpty() bool {
509         return "" == os.Getenv("GO_GCFLAGS") && t.glevel == 0
510 }
511
512 var errTimeout = errors.New("command exceeded time limit")
513
514 // run runs a test.
515 func (t *test) run() {
516         start := time.Now()
517         defer func() {
518                 t.dt = time.Since(start)
519                 close(t.donec)
520         }()
521
522         if t.glevel > 0 && !*force {
523                 // Files excluded from generics testing.
524                 filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows
525                 if excludedFiles[filename] {
526                         if *verbose {
527                                 fmt.Printf("excl\t%s\n", filename)
528                         }
529                         return
530                 }
531         }
532
533         srcBytes, err := ioutil.ReadFile(t.goFileName())
534         if err != nil {
535                 t.err = err
536                 return
537         }
538         t.src = string(srcBytes)
539         if t.src[0] == '\n' {
540                 t.err = skipError("starts with newline")
541                 return
542         }
543
544         // Execution recipe stops at first blank line.
545         pos := strings.Index(t.src, "\n\n")
546         if pos == -1 {
547                 t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName())
548                 return
549         }
550         action := t.src[:pos]
551         if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") {
552                 // skip first line
553                 action = action[nl+1:]
554         }
555         action = strings.TrimPrefix(action, "//")
556
557         // Check for build constraints only up to the actual code.
558         pkgPos := strings.Index(t.src, "\npackage")
559         if pkgPos == -1 {
560                 pkgPos = pos // some files are intentionally malformed
561         }
562         if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok {
563                 if *showSkips {
564                         fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why)
565                 }
566                 return
567         }
568
569         var args, flags, runenv []string
570         var tim int
571         wantError := false
572         wantAuto := false
573         singlefilepkgs := false
574         setpkgpaths := false
575         localImports := true
576         f, err := splitQuoted(action)
577         if err != nil {
578                 t.err = fmt.Errorf("invalid test recipe: %v", err)
579                 return
580         }
581         if len(f) > 0 {
582                 action = f[0]
583                 args = f[1:]
584         }
585
586         // TODO: Clean up/simplify this switch statement.
587         switch action {
588         case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "runindir", "asmcheck":
589                 // nothing to do
590         case "errorcheckandrundir":
591                 wantError = false // should be no error if also will run
592         case "errorcheckwithauto":
593                 action = "errorcheck"
594                 wantAuto = true
595                 wantError = true
596         case "errorcheck", "errorcheckdir", "errorcheckoutput":
597                 wantError = true
598         case "skip":
599                 if *runSkips {
600                         break
601                 }
602                 return
603         default:
604                 t.err = skipError("skipped; unknown pattern: " + action)
605                 return
606         }
607
608         // collect flags
609         for len(args) > 0 && strings.HasPrefix(args[0], "-") {
610                 switch args[0] {
611                 case "-1":
612                         wantError = true
613                 case "-0":
614                         wantError = false
615                 case "-s":
616                         singlefilepkgs = true
617                 case "-P":
618                         setpkgpaths = true
619                 case "-n":
620                         // Do not set relative path for local imports to current dir,
621                         // e.g. do not pass -D . -I . to the compiler.
622                         // Used in fixedbugs/bug345.go to allow compilation and import of local pkg.
623                         // See golang.org/issue/25635
624                         localImports = false
625                 case "-t": // timeout in seconds
626                         args = args[1:]
627                         var err error
628                         tim, err = strconv.Atoi(args[0])
629                         if err != nil {
630                                 t.err = fmt.Errorf("need number of seconds for -t timeout, got %s instead", args[0])
631                         }
632                 case "-goexperiment": // set GOEXPERIMENT environment
633                         args = args[1:]
634                         runenv = append(runenv, "GOEXPERIMENT="+args[0])
635
636                 default:
637                         flags = append(flags, args[0])
638                 }
639                 args = args[1:]
640         }
641         if action == "errorcheck" {
642                 found := false
643                 for i, f := range flags {
644                         if strings.HasPrefix(f, "-d=") {
645                                 flags[i] = f + ",ssa/check/on"
646                                 found = true
647                                 break
648                         }
649                 }
650                 if !found {
651                         flags = append(flags, "-d=ssa/check/on")
652                 }
653         }
654
655         type Tool int
656
657         const (
658                 _ Tool = iota
659                 AsmCheck
660                 Build
661                 Run
662                 Compile
663         )
664
665         // validForGLevel reports whether the current test is valid to run
666         // at the specified -G level. If so, it may update flags as
667         // necessary to test with -G.
668         validForGLevel := func(tool Tool) bool {
669                 if t.glevel == 0 {
670                         // default -G level; always valid
671                         return true
672                 }
673
674                 for _, flag := range flags {
675                         if strings.Contains(flag, "-G") {
676                                 // test provides explicit -G flag already
677                                 if *verbose {
678                                         fmt.Printf("excl\t%s\n", t.goFileName())
679                                 }
680                                 return false
681                         }
682                 }
683
684                 switch tool {
685                 case Build, Run:
686                         // ok; handled in goGcflags
687
688                 case Compile:
689                         flags = append(flags, fmt.Sprintf("-G=%v", t.glevel))
690
691                 default:
692                         // we don't know how to add -G for this test yet
693                         if *verbose {
694                                 fmt.Printf("excl\t%s\n", t.goFileName())
695                         }
696                         return false
697                 }
698
699                 return true
700         }
701
702         t.makeTempDir()
703         if !*keep {
704                 defer os.RemoveAll(t.tempDir)
705         }
706
707         err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
708         if err != nil {
709                 log.Fatal(err)
710         }
711
712         // A few tests (of things like the environment) require these to be set.
713         if os.Getenv("GOOS") == "" {
714                 os.Setenv("GOOS", runtime.GOOS)
715         }
716         if os.Getenv("GOARCH") == "" {
717                 os.Setenv("GOARCH", runtime.GOARCH)
718         }
719
720         var (
721                 runInDir        = t.tempDir
722                 tempDirIsGOPATH = false
723         )
724         runcmd := func(args ...string) ([]byte, error) {
725                 cmd := exec.Command(args[0], args[1:]...)
726                 var buf bytes.Buffer
727                 cmd.Stdout = &buf
728                 cmd.Stderr = &buf
729                 cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=")
730                 if runInDir != "" {
731                         cmd.Dir = runInDir
732                         // Set PWD to match Dir to speed up os.Getwd in the child process.
733                         cmd.Env = append(cmd.Env, "PWD="+cmd.Dir)
734                 }
735                 if tempDirIsGOPATH {
736                         cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
737                 }
738                 cmd.Env = append(cmd.Env, runenv...)
739
740                 var err error
741
742                 if tim != 0 {
743                         err = cmd.Start()
744                         // This command-timeout code adapted from cmd/go/test.go
745                         if err == nil {
746                                 tick := time.NewTimer(time.Duration(tim) * time.Second)
747                                 done := make(chan error)
748                                 go func() {
749                                         done <- cmd.Wait()
750                                 }()
751                                 select {
752                                 case err = <-done:
753                                         // ok
754                                 case <-tick.C:
755                                         cmd.Process.Signal(os.Interrupt)
756                                         time.Sleep(1 * time.Second)
757                                         cmd.Process.Kill()
758                                         <-done
759                                         err = errTimeout
760                                 }
761                                 tick.Stop()
762                         }
763                 } else {
764                         err = cmd.Run()
765                 }
766                 if err != nil && err != errTimeout {
767                         err = fmt.Errorf("%s\n%s", err, buf.Bytes())
768                 }
769                 return buf.Bytes(), err
770         }
771
772         long := filepath.Join(cwd, t.goFileName())
773         switch action {
774         default:
775                 t.err = fmt.Errorf("unimplemented action %q", action)
776
777         case "asmcheck":
778                 if !validForGLevel(AsmCheck) {
779                         return
780                 }
781
782                 // Compile Go file and match the generated assembly
783                 // against a set of regexps in comments.
784                 ops := t.wantedAsmOpcodes(long)
785                 self := runtime.GOOS + "/" + runtime.GOARCH
786                 for _, env := range ops.Envs() {
787                         // Only run checks relevant to the current GOOS/GOARCH,
788                         // to avoid triggering a cross-compile of the runtime.
789                         if string(env) != self && !strings.HasPrefix(string(env), self+"/") && !*allCodegen {
790                                 continue
791                         }
792                         // -S=2 forces outermost line numbers when disassembling inlined code.
793                         cmdline := []string{"build", "-gcflags", "-S=2"}
794
795                         // Append flags, but don't override -gcflags=-S=2; add to it instead.
796                         for i := 0; i < len(flags); i++ {
797                                 flag := flags[i]
798                                 switch {
799                                 case strings.HasPrefix(flag, "-gcflags="):
800                                         cmdline[2] += " " + strings.TrimPrefix(flag, "-gcflags=")
801                                 case strings.HasPrefix(flag, "--gcflags="):
802                                         cmdline[2] += " " + strings.TrimPrefix(flag, "--gcflags=")
803                                 case flag == "-gcflags", flag == "--gcflags":
804                                         i++
805                                         if i < len(flags) {
806                                                 cmdline[2] += " " + flags[i]
807                                         }
808                                 default:
809                                         cmdline = append(cmdline, flag)
810                                 }
811                         }
812
813                         cmdline = append(cmdline, long)
814                         cmd := exec.Command(goTool(), cmdline...)
815                         cmd.Env = append(os.Environ(), env.Environ()...)
816                         if len(flags) > 0 && flags[0] == "-race" {
817                                 cmd.Env = append(cmd.Env, "CGO_ENABLED=1")
818                         }
819
820                         var buf bytes.Buffer
821                         cmd.Stdout, cmd.Stderr = &buf, &buf
822                         if err := cmd.Run(); err != nil {
823                                 fmt.Println(env, "\n", cmd.Stderr)
824                                 t.err = err
825                                 return
826                         }
827
828                         t.err = t.asmCheck(buf.String(), long, env, ops[env])
829                         if t.err != nil {
830                                 return
831                         }
832                 }
833                 return
834
835         case "errorcheck":
836                 if !validForGLevel(Compile) {
837                         return
838                 }
839
840                 // Compile Go file.
841                 // Fail if wantError is true and compilation was successful and vice versa.
842                 // Match errors produced by gc against errors in comments.
843                 // TODO(gri) remove need for -C (disable printing of columns in error messages)
844                 cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-C", "-e", "-o", "a.o"}
845                 // No need to add -dynlink even if linkshared if we're just checking for errors...
846                 cmdline = append(cmdline, flags...)
847                 cmdline = append(cmdline, long)
848                 out, err := runcmd(cmdline...)
849                 if wantError {
850                         if err == nil {
851                                 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
852                                 return
853                         }
854                         if err == errTimeout {
855                                 t.err = fmt.Errorf("compilation timed out")
856                                 return
857                         }
858                 } else {
859                         if err != nil {
860                                 t.err = err
861                                 return
862                         }
863                 }
864                 if *updateErrors {
865                         t.updateErrors(string(out), long)
866                 }
867                 t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
868
869         case "compile":
870                 if !validForGLevel(Compile) {
871                         return
872                 }
873
874                 // Compile Go file.
875                 _, t.err = compileFile(runcmd, long, flags)
876
877         case "compiledir":
878                 if !validForGLevel(Compile) {
879                         return
880                 }
881
882                 // Compile all files in the directory as packages in lexicographic order.
883                 longdir := filepath.Join(cwd, t.goDirName())
884                 pkgs, err := goDirPackages(longdir, singlefilepkgs)
885                 if err != nil {
886                         t.err = err
887                         return
888                 }
889                 for _, gofiles := range pkgs {
890                         _, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...)
891                         if t.err != nil {
892                                 return
893                         }
894                 }
895
896         case "errorcheckdir", "errorcheckandrundir":
897                 if !validForGLevel(Compile) {
898                         return
899                 }
900
901                 flags = append(flags, "-d=panic")
902                 // Compile and errorCheck all files in the directory as packages in lexicographic order.
903                 // If errorcheckdir and wantError, compilation of the last package must fail.
904                 // If errorcheckandrundir and wantError, compilation of the package prior the last must fail.
905                 longdir := filepath.Join(cwd, t.goDirName())
906                 pkgs, err := goDirPackages(longdir, singlefilepkgs)
907                 if err != nil {
908                         t.err = err
909                         return
910                 }
911                 errPkg := len(pkgs) - 1
912                 if wantError && action == "errorcheckandrundir" {
913                         // The last pkg should compiled successfully and will be run in next case.
914                         // Preceding pkg must return an error from compileInDir.
915                         errPkg--
916                 }
917                 for i, gofiles := range pkgs {
918                         out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...)
919                         if i == errPkg {
920                                 if wantError && err == nil {
921                                         t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
922                                         return
923                                 } else if !wantError && err != nil {
924                                         t.err = err
925                                         return
926                                 }
927                         } else if err != nil {
928                                 t.err = err
929                                 return
930                         }
931                         var fullshort []string
932                         for _, name := range gofiles {
933                                 fullshort = append(fullshort, filepath.Join(longdir, name), name)
934                         }
935                         t.err = t.errorCheck(string(out), wantAuto, fullshort...)
936                         if t.err != nil {
937                                 break
938                         }
939                 }
940                 if action == "errorcheckdir" {
941                         return
942                 }
943                 fallthrough
944
945         case "rundir":
946                 if !validForGLevel(Run) {
947                         return
948                 }
949
950                 // Compile all files in the directory as packages in lexicographic order.
951                 // In case of errorcheckandrundir, ignore failed compilation of the package before the last.
952                 // Link as if the last file is the main package, run it.
953                 // Verify the expected output.
954                 longdir := filepath.Join(cwd, t.goDirName())
955                 pkgs, err := goDirPackages(longdir, singlefilepkgs)
956                 if err != nil {
957                         t.err = err
958                         return
959                 }
960                 // Split flags into gcflags and ldflags
961                 ldflags := []string{}
962                 for i, fl := range flags {
963                         if fl == "-ldflags" {
964                                 ldflags = flags[i+1:]
965                                 flags = flags[0:i]
966                                 break
967                         }
968                 }
969
970                 for i, gofiles := range pkgs {
971                         pflags := []string{}
972                         pflags = append(pflags, flags...)
973                         if setpkgpaths {
974                                 fp := filepath.Join(longdir, gofiles[0])
975                                 pkgname, err := getPackageNameFromSource(fp)
976                                 if err != nil {
977                                         log.Fatal(err)
978                                 }
979                                 pflags = append(pflags, "-p", pkgname)
980                         }
981                         _, err := compileInDir(runcmd, longdir, pflags, localImports, gofiles...)
982                         // Allow this package compilation fail based on conditions below;
983                         // its errors were checked in previous case.
984                         if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) {
985                                 t.err = err
986                                 return
987                         }
988                         if i == len(pkgs)-1 {
989                                 err = linkFile(runcmd, gofiles[0], ldflags)
990                                 if err != nil {
991                                         t.err = err
992                                         return
993                                 }
994                                 var cmd []string
995                                 cmd = append(cmd, findExecCmd()...)
996                                 cmd = append(cmd, filepath.Join(t.tempDir, "a.exe"))
997                                 cmd = append(cmd, args...)
998                                 out, err := runcmd(cmd...)
999                                 if err != nil {
1000                                         t.err = err
1001                                         return
1002                                 }
1003                                 t.checkExpectedOutput(out)
1004                         }
1005                 }
1006
1007         case "runindir":
1008                 if !validForGLevel(Run) {
1009                         return
1010                 }
1011
1012                 // Make a shallow copy of t.goDirName() in its own module and GOPATH, and
1013                 // run "go run ." in it. The module path (and hence import path prefix) of
1014                 // the copy is equal to the basename of the source directory.
1015                 //
1016                 // It's used when test a requires a full 'go build' in order to compile
1017                 // the sources, such as when importing multiple packages (issue29612.dir)
1018                 // or compiling a package containing assembly files (see issue15609.dir),
1019                 // but still needs to be run to verify the expected output.
1020                 tempDirIsGOPATH = true
1021                 srcDir := t.goDirName()
1022                 modName := filepath.Base(srcDir)
1023                 gopathSrcDir := filepath.Join(t.tempDir, "src", modName)
1024                 runInDir = gopathSrcDir
1025
1026                 if err := overlayDir(gopathSrcDir, srcDir); err != nil {
1027                         t.err = err
1028                         return
1029                 }
1030
1031                 modFile := fmt.Sprintf("module %s\ngo 1.14\n", modName)
1032                 if err := ioutil.WriteFile(filepath.Join(gopathSrcDir, "go.mod"), []byte(modFile), 0666); err != nil {
1033                         t.err = err
1034                         return
1035                 }
1036
1037                 cmd := []string{goTool(), "run", t.goGcflags()}
1038                 if *linkshared {
1039                         cmd = append(cmd, "-linkshared")
1040                 }
1041                 cmd = append(cmd, flags...)
1042                 cmd = append(cmd, ".")
1043                 out, err := runcmd(cmd...)
1044                 if err != nil {
1045                         t.err = err
1046                         return
1047                 }
1048                 t.checkExpectedOutput(out)
1049
1050         case "build":
1051                 if !validForGLevel(Build) {
1052                         return
1053                 }
1054
1055                 // Build Go file.
1056                 _, err := runcmd(goTool(), "build", t.goGcflags(), "-o", "a.exe", long)
1057                 if err != nil {
1058                         t.err = err
1059                 }
1060
1061         case "builddir", "buildrundir":
1062                 if !validForGLevel(Build) {
1063                         return
1064                 }
1065
1066                 // Build an executable from all the .go and .s files in a subdirectory.
1067                 // Run it and verify its output in the buildrundir case.
1068                 longdir := filepath.Join(cwd, t.goDirName())
1069                 files, dirErr := ioutil.ReadDir(longdir)
1070                 if dirErr != nil {
1071                         t.err = dirErr
1072                         break
1073                 }
1074                 var gos []string
1075                 var asms []string
1076                 for _, file := range files {
1077                         switch filepath.Ext(file.Name()) {
1078                         case ".go":
1079                                 gos = append(gos, filepath.Join(longdir, file.Name()))
1080                         case ".s":
1081                                 asms = append(asms, filepath.Join(longdir, file.Name()))
1082                         }
1083
1084                 }
1085                 if len(asms) > 0 {
1086                         emptyHdrFile := filepath.Join(t.tempDir, "go_asm.h")
1087                         if err := ioutil.WriteFile(emptyHdrFile, nil, 0666); err != nil {
1088                                 t.err = fmt.Errorf("write empty go_asm.h: %s", err)
1089                                 return
1090                         }
1091                         cmd := []string{goTool(), "tool", "asm", "-gensymabis", "-o", "symabis"}
1092                         cmd = append(cmd, asms...)
1093                         _, err = runcmd(cmd...)
1094                         if err != nil {
1095                                 t.err = err
1096                                 break
1097                         }
1098                 }
1099                 var objs []string
1100                 cmd := []string{goTool(), "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"}
1101                 if len(asms) > 0 {
1102                         cmd = append(cmd, "-asmhdr", "go_asm.h", "-symabis", "symabis")
1103                 }
1104                 cmd = append(cmd, gos...)
1105                 _, err := runcmd(cmd...)
1106                 if err != nil {
1107                         t.err = err
1108                         break
1109                 }
1110                 objs = append(objs, "go.o")
1111                 if len(asms) > 0 {
1112                         cmd = []string{goTool(), "tool", "asm", "-e", "-I", ".", "-o", "asm.o"}
1113                         cmd = append(cmd, asms...)
1114                         _, err = runcmd(cmd...)
1115                         if err != nil {
1116                                 t.err = err
1117                                 break
1118                         }
1119                         objs = append(objs, "asm.o")
1120                 }
1121                 cmd = []string{goTool(), "tool", "pack", "c", "all.a"}
1122                 cmd = append(cmd, objs...)
1123                 _, err = runcmd(cmd...)
1124                 if err != nil {
1125                         t.err = err
1126                         break
1127                 }
1128                 cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"}
1129                 _, err = runcmd(cmd...)
1130                 if err != nil {
1131                         t.err = err
1132                         break
1133                 }
1134                 if action == "buildrundir" {
1135                         cmd = append(findExecCmd(), filepath.Join(t.tempDir, "a.exe"))
1136                         out, err := runcmd(cmd...)
1137                         if err != nil {
1138                                 t.err = err
1139                                 break
1140                         }
1141                         t.checkExpectedOutput(out)
1142                 }
1143
1144         case "buildrun":
1145                 if !validForGLevel(Build) {
1146                         return
1147                 }
1148
1149                 // Build an executable from Go file, then run it, verify its output.
1150                 // Useful for timeout tests where failure mode is infinite loop.
1151                 // TODO: not supported on NaCl
1152                 cmd := []string{goTool(), "build", t.goGcflags(), "-o", "a.exe"}
1153                 if *linkshared {
1154                         cmd = append(cmd, "-linkshared")
1155                 }
1156                 longdirgofile := filepath.Join(filepath.Join(cwd, t.dir), t.gofile)
1157                 cmd = append(cmd, flags...)
1158                 cmd = append(cmd, longdirgofile)
1159                 _, err := runcmd(cmd...)
1160                 if err != nil {
1161                         t.err = err
1162                         return
1163                 }
1164                 cmd = []string{"./a.exe"}
1165                 out, err := runcmd(append(cmd, args...)...)
1166                 if err != nil {
1167                         t.err = err
1168                         return
1169                 }
1170
1171                 t.checkExpectedOutput(out)
1172
1173         case "run":
1174                 if !validForGLevel(Run) {
1175                         return
1176                 }
1177
1178                 // Run Go file if no special go command flags are provided;
1179                 // otherwise build an executable and run it.
1180                 // Verify the output.
1181                 runInDir = ""
1182                 var out []byte
1183                 var err error
1184                 if len(flags)+len(args) == 0 && t.goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
1185                         // If we're not using special go command flags,
1186                         // skip all the go command machinery.
1187                         // This avoids any time the go command would
1188                         // spend checking whether, for example, the installed
1189                         // package runtime is up to date.
1190                         // Because we run lots of trivial test programs,
1191                         // the time adds up.
1192                         pkg := filepath.Join(t.tempDir, "pkg.a")
1193                         if _, err := runcmd(goTool(), "tool", "compile", "-o", pkg, t.goFileName()); err != nil {
1194                                 t.err = err
1195                                 return
1196                         }
1197                         exe := filepath.Join(t.tempDir, "test.exe")
1198                         cmd := []string{goTool(), "tool", "link", "-s", "-w"}
1199                         cmd = append(cmd, "-o", exe, pkg)
1200                         if _, err := runcmd(cmd...); err != nil {
1201                                 t.err = err
1202                                 return
1203                         }
1204                         out, err = runcmd(append([]string{exe}, args...)...)
1205                 } else {
1206                         cmd := []string{goTool(), "run", t.goGcflags()}
1207                         if *linkshared {
1208                                 cmd = append(cmd, "-linkshared")
1209                         }
1210                         cmd = append(cmd, flags...)
1211                         cmd = append(cmd, t.goFileName())
1212                         out, err = runcmd(append(cmd, args...)...)
1213                 }
1214                 if err != nil {
1215                         t.err = err
1216                         return
1217                 }
1218                 t.checkExpectedOutput(out)
1219
1220         case "runoutput":
1221                 if !validForGLevel(Run) {
1222                         return
1223                 }
1224
1225                 // Run Go file and write its output into temporary Go file.
1226                 // Run generated Go file and verify its output.
1227                 rungatec <- true
1228                 defer func() {
1229                         <-rungatec
1230                 }()
1231                 runInDir = ""
1232                 cmd := []string{goTool(), "run", t.goGcflags()}
1233                 if *linkshared {
1234                         cmd = append(cmd, "-linkshared")
1235                 }
1236                 cmd = append(cmd, t.goFileName())
1237                 out, err := runcmd(append(cmd, args...)...)
1238                 if err != nil {
1239                         t.err = err
1240                         return
1241                 }
1242                 tfile := filepath.Join(t.tempDir, "tmp__.go")
1243                 if err := ioutil.WriteFile(tfile, out, 0666); err != nil {
1244                         t.err = fmt.Errorf("write tempfile:%s", err)
1245                         return
1246                 }
1247                 cmd = []string{goTool(), "run", t.goGcflags()}
1248                 if *linkshared {
1249                         cmd = append(cmd, "-linkshared")
1250                 }
1251                 cmd = append(cmd, tfile)
1252                 out, err = runcmd(cmd...)
1253                 if err != nil {
1254                         t.err = err
1255                         return
1256                 }
1257                 t.checkExpectedOutput(out)
1258
1259         case "errorcheckoutput":
1260                 if !validForGLevel(Compile) {
1261                         return
1262                 }
1263
1264                 // Run Go file and write its output into temporary Go file.
1265                 // Compile and errorCheck generated Go file.
1266                 runInDir = ""
1267                 cmd := []string{goTool(), "run", t.goGcflags()}
1268                 if *linkshared {
1269                         cmd = append(cmd, "-linkshared")
1270                 }
1271                 cmd = append(cmd, t.goFileName())
1272                 out, err := runcmd(append(cmd, args...)...)
1273                 if err != nil {
1274                         t.err = err
1275                         return
1276                 }
1277                 tfile := filepath.Join(t.tempDir, "tmp__.go")
1278                 err = ioutil.WriteFile(tfile, out, 0666)
1279                 if err != nil {
1280                         t.err = fmt.Errorf("write tempfile:%s", err)
1281                         return
1282                 }
1283                 cmdline := []string{goTool(), "tool", "compile", "-d=panic", "-e", "-o", "a.o"}
1284                 cmdline = append(cmdline, flags...)
1285                 cmdline = append(cmdline, tfile)
1286                 out, err = runcmd(cmdline...)
1287                 if wantError {
1288                         if err == nil {
1289                                 t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
1290                                 return
1291                         }
1292                 } else {
1293                         if err != nil {
1294                                 t.err = err
1295                                 return
1296                         }
1297                 }
1298                 t.err = t.errorCheck(string(out), false, tfile, "tmp__.go")
1299                 return
1300         }
1301 }
1302
1303 var execCmd []string
1304
1305 func findExecCmd() []string {
1306         if execCmd != nil {
1307                 return execCmd
1308         }
1309         execCmd = []string{} // avoid work the second time
1310         if goos == runtime.GOOS && goarch == runtime.GOARCH {
1311                 return execCmd
1312         }
1313         path, err := exec.LookPath(fmt.Sprintf("go_%s_%s_exec", goos, goarch))
1314         if err == nil {
1315                 execCmd = []string{path}
1316         }
1317         return execCmd
1318 }
1319
1320 func (t *test) String() string {
1321         return filepath.Join(t.dir, t.gofile)
1322 }
1323
1324 func (t *test) makeTempDir() {
1325         var err error
1326         t.tempDir, err = ioutil.TempDir("", "")
1327         if err != nil {
1328                 log.Fatal(err)
1329         }
1330         if *keep {
1331                 log.Printf("Temporary directory is %s", t.tempDir)
1332         }
1333 }
1334
1335 // checkExpectedOutput compares the output from compiling and/or running with the contents
1336 // of the corresponding reference output file, if any (replace ".go" with ".out").
1337 // If they don't match, fail with an informative message.
1338 func (t *test) checkExpectedOutput(gotBytes []byte) {
1339         got := string(gotBytes)
1340         filename := filepath.Join(t.dir, t.gofile)
1341         filename = filename[:len(filename)-len(".go")]
1342         filename += ".out"
1343         b, err := ioutil.ReadFile(filename)
1344         // File is allowed to be missing (err != nil) in which case output should be empty.
1345         got = strings.Replace(got, "\r\n", "\n", -1)
1346         if got != string(b) {
1347                 if err == nil {
1348                         t.err = fmt.Errorf("output does not match expected in %s. Instead saw\n%s", filename, got)
1349                 } else {
1350                         t.err = fmt.Errorf("output should be empty when (optional) expected-output file %s is not present. Instead saw\n%s", filename, got)
1351                 }
1352         }
1353 }
1354
1355 func splitOutput(out string, wantAuto bool) []string {
1356         // gc error messages continue onto additional lines with leading tabs.
1357         // Split the output at the beginning of each line that doesn't begin with a tab.
1358         // <autogenerated> lines are impossible to match so those are filtered out.
1359         var res []string
1360         for _, line := range strings.Split(out, "\n") {
1361                 if strings.HasSuffix(line, "\r") { // remove '\r', output by compiler on windows
1362                         line = line[:len(line)-1]
1363                 }
1364                 if strings.HasPrefix(line, "\t") {
1365                         res[len(res)-1] += "\n" + line
1366                 } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") {
1367                         continue
1368                 } else if strings.TrimSpace(line) != "" {
1369                         res = append(res, line)
1370                 }
1371         }
1372         return res
1373 }
1374
1375 // errorCheck matches errors in outStr against comments in source files.
1376 // For each line of the source files which should generate an error,
1377 // there should be a comment of the form // ERROR "regexp".
1378 // If outStr has an error for a line which has no such comment,
1379 // this function will report an error.
1380 // Likewise if outStr does not have an error for a line which has a comment,
1381 // or if the error message does not match the <regexp>.
1382 // The <regexp> syntax is Perl but it's best to stick to egrep.
1383 //
1384 // Sources files are supplied as fullshort slice.
1385 // It consists of pairs: full path to source file and its base name.
1386 func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) {
1387         defer func() {
1388                 if *verbose && err != nil {
1389                         log.Printf("%s gc output:\n%s", t, outStr)
1390                 }
1391         }()
1392         var errs []error
1393         out := splitOutput(outStr, wantAuto)
1394
1395         // Cut directory name.
1396         for i := range out {
1397                 for j := 0; j < len(fullshort); j += 2 {
1398                         full, short := fullshort[j], fullshort[j+1]
1399                         out[i] = strings.Replace(out[i], full, short, -1)
1400                 }
1401         }
1402
1403         var want []wantedError
1404         for j := 0; j < len(fullshort); j += 2 {
1405                 full, short := fullshort[j], fullshort[j+1]
1406                 want = append(want, t.wantedErrors(full, short)...)
1407         }
1408
1409         for _, we := range want {
1410                 var errmsgs []string
1411                 if we.auto {
1412                         errmsgs, out = partitionStrings("<autogenerated>", out)
1413                 } else {
1414                         errmsgs, out = partitionStrings(we.prefix, out)
1415                 }
1416                 if len(errmsgs) == 0 {
1417                         errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
1418                         continue
1419                 }
1420                 matched := false
1421                 n := len(out)
1422                 for _, errmsg := range errmsgs {
1423                         // Assume errmsg says "file:line: foo".
1424                         // Cut leading "file:line: " to avoid accidental matching of file name instead of message.
1425                         text := errmsg
1426                         if i := strings.Index(text, " "); i >= 0 {
1427                                 text = text[i+1:]
1428                         }
1429                         if we.re.MatchString(text) {
1430                                 matched = true
1431                         } else {
1432                                 out = append(out, errmsg)
1433                         }
1434                 }
1435                 if !matched {
1436                         errs = append(errs, fmt.Errorf("%s:%d: no match for %#q in:\n\t%s", we.file, we.lineNum, we.reStr, strings.Join(out[n:], "\n\t")))
1437                         continue
1438                 }
1439         }
1440
1441         if len(out) > 0 {
1442                 errs = append(errs, fmt.Errorf("Unmatched Errors:"))
1443                 for _, errLine := range out {
1444                         errs = append(errs, fmt.Errorf("%s", errLine))
1445                 }
1446         }
1447
1448         if len(errs) == 0 {
1449                 return nil
1450         }
1451         if len(errs) == 1 {
1452                 return errs[0]
1453         }
1454         var buf bytes.Buffer
1455         fmt.Fprintf(&buf, "\n")
1456         for _, err := range errs {
1457                 fmt.Fprintf(&buf, "%s\n", err.Error())
1458         }
1459         return errors.New(buf.String())
1460 }
1461
1462 func (t *test) updateErrors(out, file string) {
1463         base := path.Base(file)
1464         // Read in source file.
1465         src, err := ioutil.ReadFile(file)
1466         if err != nil {
1467                 fmt.Fprintln(os.Stderr, err)
1468                 return
1469         }
1470         lines := strings.Split(string(src), "\n")
1471         // Remove old errors.
1472         for i, ln := range lines {
1473                 pos := strings.Index(ln, " // ERROR ")
1474                 if pos >= 0 {
1475                         lines[i] = ln[:pos]
1476                 }
1477         }
1478         // Parse new errors.
1479         errors := make(map[int]map[string]bool)
1480         tmpRe := regexp.MustCompile(`autotmp_[0-9]+`)
1481         for _, errStr := range splitOutput(out, false) {
1482                 colon1 := strings.Index(errStr, ":")
1483                 if colon1 < 0 || errStr[:colon1] != file {
1484                         continue
1485                 }
1486                 colon2 := strings.Index(errStr[colon1+1:], ":")
1487                 if colon2 < 0 {
1488                         continue
1489                 }
1490                 colon2 += colon1 + 1
1491                 line, err := strconv.Atoi(errStr[colon1+1 : colon2])
1492                 line--
1493                 if err != nil || line < 0 || line >= len(lines) {
1494                         continue
1495                 }
1496                 msg := errStr[colon2+2:]
1497                 msg = strings.Replace(msg, file, base, -1) // normalize file mentions in error itself
1498                 msg = strings.TrimLeft(msg, " \t")
1499                 for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} {
1500                         msg = strings.Replace(msg, r, `\`+r, -1)
1501                 }
1502                 msg = strings.Replace(msg, `"`, `.`, -1)
1503                 msg = tmpRe.ReplaceAllLiteralString(msg, `autotmp_[0-9]+`)
1504                 if errors[line] == nil {
1505                         errors[line] = make(map[string]bool)
1506                 }
1507                 errors[line][msg] = true
1508         }
1509         // Add new errors.
1510         for line, errs := range errors {
1511                 var sorted []string
1512                 for e := range errs {
1513                         sorted = append(sorted, e)
1514                 }
1515                 sort.Strings(sorted)
1516                 lines[line] += " // ERROR"
1517                 for _, e := range sorted {
1518                         lines[line] += fmt.Sprintf(` "%s$"`, e)
1519                 }
1520         }
1521         // Write new file.
1522         err = ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0640)
1523         if err != nil {
1524                 fmt.Fprintln(os.Stderr, err)
1525                 return
1526         }
1527         // Polish.
1528         exec.Command(goTool(), "fmt", file).CombinedOutput()
1529 }
1530
1531 // matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[),
1532 // That is, it needs the file name prefix followed by a : or a [,
1533 // and possibly preceded by a directory name.
1534 func matchPrefix(s, prefix string) bool {
1535         i := strings.Index(s, ":")
1536         if i < 0 {
1537                 return false
1538         }
1539         j := strings.LastIndex(s[:i], "/")
1540         s = s[j+1:]
1541         if len(s) <= len(prefix) || s[:len(prefix)] != prefix {
1542                 return false
1543         }
1544         switch s[len(prefix)] {
1545         case '[', ':':
1546                 return true
1547         }
1548         return false
1549 }
1550
1551 func partitionStrings(prefix string, strs []string) (matched, unmatched []string) {
1552         for _, s := range strs {
1553                 if matchPrefix(s, prefix) {
1554                         matched = append(matched, s)
1555                 } else {
1556                         unmatched = append(unmatched, s)
1557                 }
1558         }
1559         return
1560 }
1561
1562 type wantedError struct {
1563         reStr   string
1564         re      *regexp.Regexp
1565         lineNum int
1566         auto    bool // match <autogenerated> line
1567         file    string
1568         prefix  string
1569 }
1570
1571 var (
1572         errRx       = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
1573         errAutoRx   = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`)
1574         errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
1575         lineRx      = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
1576 )
1577
1578 func (t *test) wantedErrors(file, short string) (errs []wantedError) {
1579         cache := make(map[string]*regexp.Regexp)
1580
1581         src, _ := ioutil.ReadFile(file)
1582         for i, line := range strings.Split(string(src), "\n") {
1583                 lineNum := i + 1
1584                 if strings.Contains(line, "////") {
1585                         // double comment disables ERROR
1586                         continue
1587                 }
1588                 var auto bool
1589                 m := errAutoRx.FindStringSubmatch(line)
1590                 if m != nil {
1591                         auto = true
1592                 } else {
1593                         m = errRx.FindStringSubmatch(line)
1594                 }
1595                 if m == nil {
1596                         continue
1597                 }
1598                 all := m[1]
1599                 mm := errQuotesRx.FindAllStringSubmatch(all, -1)
1600                 if mm == nil {
1601                         log.Fatalf("%s:%d: invalid errchk line: %s", t.goFileName(), lineNum, line)
1602                 }
1603                 for _, m := range mm {
1604                         rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string {
1605                                 n := lineNum
1606                                 if strings.HasPrefix(m, "LINE+") {
1607                                         delta, _ := strconv.Atoi(m[5:])
1608                                         n += delta
1609                                 } else if strings.HasPrefix(m, "LINE-") {
1610                                         delta, _ := strconv.Atoi(m[5:])
1611                                         n -= delta
1612                                 }
1613                                 return fmt.Sprintf("%s:%d", short, n)
1614                         })
1615                         re := cache[rx]
1616                         if re == nil {
1617                                 var err error
1618                                 re, err = regexp.Compile(rx)
1619                                 if err != nil {
1620                                         log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err)
1621                                 }
1622                                 cache[rx] = re
1623                         }
1624                         prefix := fmt.Sprintf("%s:%d", short, lineNum)
1625                         errs = append(errs, wantedError{
1626                                 reStr:   rx,
1627                                 re:      re,
1628                                 prefix:  prefix,
1629                                 auto:    auto,
1630                                 lineNum: lineNum,
1631                                 file:    short,
1632                         })
1633                 }
1634         }
1635
1636         return
1637 }
1638
1639 const (
1640         // Regexp to match a single opcode check: optionally begin with "-" (to indicate
1641         // a negative check), followed by a string literal enclosed in "" or ``. For "",
1642         // backslashes must be handled.
1643         reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")`
1644 )
1645
1646 var (
1647         // Regexp to split a line in code and comment, trimming spaces
1648         rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?://\s*(.+)\s*)?$`)
1649
1650         // Regexp to extract an architecture check: architecture name (or triplet),
1651         // followed by semi-colon, followed by a comma-separated list of opcode checks.
1652         // Extraneous spaces are ignored.
1653         rxAsmPlatform = regexp.MustCompile(`(\w+)(/\w+)?(/\w*)?\s*:\s*(` + reMatchCheck + `(?:\s*,\s*` + reMatchCheck + `)*)`)
1654
1655         // Regexp to extract a single opcoded check
1656         rxAsmCheck = regexp.MustCompile(reMatchCheck)
1657
1658         // List of all architecture variants. Key is the GOARCH architecture,
1659         // value[0] is the variant-changing environment variable, and values[1:]
1660         // are the supported variants.
1661         archVariants = map[string][]string{
1662                 "386":     {"GO386", "sse2", "softfloat"},
1663                 "amd64":   {},
1664                 "arm":     {"GOARM", "5", "6", "7"},
1665                 "arm64":   {},
1666                 "mips":    {"GOMIPS", "hardfloat", "softfloat"},
1667                 "mips64":  {"GOMIPS64", "hardfloat", "softfloat"},
1668                 "ppc64":   {"GOPPC64", "power8", "power9"},
1669                 "ppc64le": {"GOPPC64", "power8", "power9"},
1670                 "s390x":   {},
1671                 "wasm":    {},
1672         }
1673 )
1674
1675 // wantedAsmOpcode is a single asmcheck check
1676 type wantedAsmOpcode struct {
1677         fileline string         // original source file/line (eg: "/path/foo.go:45")
1678         line     int            // original source line
1679         opcode   *regexp.Regexp // opcode check to be performed on assembly output
1680         negative bool           // true if the check is supposed to fail rather than pass
1681         found    bool           // true if the opcode check matched at least one in the output
1682 }
1683
1684 // A build environment triplet separated by slashes (eg: linux/386/sse2).
1685 // The third field can be empty if the arch does not support variants (eg: "plan9/amd64/")
1686 type buildEnv string
1687
1688 // Environ returns the environment it represents in cmd.Environ() "key=val" format
1689 // For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"}
1690 func (b buildEnv) Environ() []string {
1691         fields := strings.Split(string(b), "/")
1692         if len(fields) != 3 {
1693                 panic("invalid buildEnv string: " + string(b))
1694         }
1695         env := []string{"GOOS=" + fields[0], "GOARCH=" + fields[1]}
1696         if fields[2] != "" {
1697                 env = append(env, archVariants[fields[1]][0]+"="+fields[2])
1698         }
1699         return env
1700 }
1701
1702 // asmChecks represents all the asmcheck checks present in a test file
1703 // The outer map key is the build triplet in which the checks must be performed.
1704 // The inner map key represent the source file line ("filename.go:1234") at which the
1705 // checks must be performed.
1706 type asmChecks map[buildEnv]map[string][]wantedAsmOpcode
1707
1708 // Envs returns all the buildEnv in which at least one check is present
1709 func (a asmChecks) Envs() []buildEnv {
1710         var envs []buildEnv
1711         for e := range a {
1712                 envs = append(envs, e)
1713         }
1714         sort.Slice(envs, func(i, j int) bool {
1715                 return string(envs[i]) < string(envs[j])
1716         })
1717         return envs
1718 }
1719
1720 func (t *test) wantedAsmOpcodes(fn string) asmChecks {
1721         ops := make(asmChecks)
1722
1723         comment := ""
1724         src, _ := ioutil.ReadFile(fn)
1725         for i, line := range strings.Split(string(src), "\n") {
1726                 matches := rxAsmComment.FindStringSubmatch(line)
1727                 code, cmt := matches[1], matches[2]
1728
1729                 // Keep comments pending in the comment variable until
1730                 // we find a line that contains some code.
1731                 comment += " " + cmt
1732                 if code == "" {
1733                         continue
1734                 }
1735
1736                 // Parse and extract any architecture check from comments,
1737                 // made by one architecture name and multiple checks.
1738                 lnum := fn + ":" + strconv.Itoa(i+1)
1739                 for _, ac := range rxAsmPlatform.FindAllStringSubmatch(comment, -1) {
1740                         archspec, allchecks := ac[1:4], ac[4]
1741
1742                         var arch, subarch, os string
1743                         switch {
1744                         case archspec[2] != "": // 3 components: "linux/386/sse2"
1745                                 os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:]
1746                         case archspec[1] != "": // 2 components: "386/sse2"
1747                                 os, arch, subarch = "linux", archspec[0], archspec[1][1:]
1748                         default: // 1 component: "386"
1749                                 os, arch, subarch = "linux", archspec[0], ""
1750                                 if arch == "wasm" {
1751                                         os = "js"
1752                                 }
1753                         }
1754
1755                         if _, ok := archVariants[arch]; !ok {
1756                                 log.Fatalf("%s:%d: unsupported architecture: %v", t.goFileName(), i+1, arch)
1757                         }
1758
1759                         // Create the build environments corresponding the above specifiers
1760                         envs := make([]buildEnv, 0, 4)
1761                         if subarch != "" {
1762                                 envs = append(envs, buildEnv(os+"/"+arch+"/"+subarch))
1763                         } else {
1764                                 subarchs := archVariants[arch]
1765                                 if len(subarchs) == 0 {
1766                                         envs = append(envs, buildEnv(os+"/"+arch+"/"))
1767                                 } else {
1768                                         for _, sa := range archVariants[arch][1:] {
1769                                                 envs = append(envs, buildEnv(os+"/"+arch+"/"+sa))
1770                                         }
1771                                 }
1772                         }
1773
1774                         for _, m := range rxAsmCheck.FindAllString(allchecks, -1) {
1775                                 negative := false
1776                                 if m[0] == '-' {
1777                                         negative = true
1778                                         m = m[1:]
1779                                 }
1780
1781                                 rxsrc, err := strconv.Unquote(m)
1782                                 if err != nil {
1783                                         log.Fatalf("%s:%d: error unquoting string: %v", t.goFileName(), i+1, err)
1784                                 }
1785
1786                                 // Compile the checks as regular expressions. Notice that we
1787                                 // consider checks as matching from the beginning of the actual
1788                                 // assembler source (that is, what is left on each line of the
1789                                 // compile -S output after we strip file/line info) to avoid
1790                                 // trivial bugs such as "ADD" matching "FADD". This
1791                                 // doesn't remove genericity: it's still possible to write
1792                                 // something like "F?ADD", but we make common cases simpler
1793                                 // to get right.
1794                                 oprx, err := regexp.Compile("^" + rxsrc)
1795                                 if err != nil {
1796                                         log.Fatalf("%s:%d: %v", t.goFileName(), i+1, err)
1797                                 }
1798
1799                                 for _, env := range envs {
1800                                         if ops[env] == nil {
1801                                                 ops[env] = make(map[string][]wantedAsmOpcode)
1802                                         }
1803                                         ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{
1804                                                 negative: negative,
1805                                                 fileline: lnum,
1806                                                 line:     i + 1,
1807                                                 opcode:   oprx,
1808                                         })
1809                                 }
1810                         }
1811                 }
1812                 comment = ""
1813         }
1814
1815         return ops
1816 }
1817
1818 func (t *test) asmCheck(outStr string, fn string, env buildEnv, fullops map[string][]wantedAsmOpcode) (err error) {
1819         // The assembly output contains the concatenated dump of multiple functions.
1820         // the first line of each function begins at column 0, while the rest is
1821         // indented by a tabulation. These data structures help us index the
1822         // output by function.
1823         functionMarkers := make([]int, 1)
1824         lineFuncMap := make(map[string]int)
1825
1826         lines := strings.Split(outStr, "\n")
1827         rxLine := regexp.MustCompile(fmt.Sprintf(`\((%s:\d+)\)\s+(.*)`, regexp.QuoteMeta(fn)))
1828
1829         for nl, line := range lines {
1830                 // Check if this line begins a function
1831                 if len(line) > 0 && line[0] != '\t' {
1832                         functionMarkers = append(functionMarkers, nl)
1833                 }
1834
1835                 // Search if this line contains a assembly opcode (which is prefixed by the
1836                 // original source file/line in parenthesis)
1837                 matches := rxLine.FindStringSubmatch(line)
1838                 if len(matches) == 0 {
1839                         continue
1840                 }
1841                 srcFileLine, asm := matches[1], matches[2]
1842
1843                 // Associate the original file/line information to the current
1844                 // function in the output; it will be useful to dump it in case
1845                 // of error.
1846                 lineFuncMap[srcFileLine] = len(functionMarkers) - 1
1847
1848                 // If there are opcode checks associated to this source file/line,
1849                 // run the checks.
1850                 if ops, found := fullops[srcFileLine]; found {
1851                         for i := range ops {
1852                                 if !ops[i].found && ops[i].opcode.FindString(asm) != "" {
1853                                         ops[i].found = true
1854                                 }
1855                         }
1856                 }
1857         }
1858         functionMarkers = append(functionMarkers, len(lines))
1859
1860         var failed []wantedAsmOpcode
1861         for _, ops := range fullops {
1862                 for _, o := range ops {
1863                         // There's a failure if a negative match was found,
1864                         // or a positive match was not found.
1865                         if o.negative == o.found {
1866                                 failed = append(failed, o)
1867                         }
1868                 }
1869         }
1870         if len(failed) == 0 {
1871                 return
1872         }
1873
1874         // At least one asmcheck failed; report them
1875         sort.Slice(failed, func(i, j int) bool {
1876                 return failed[i].line < failed[j].line
1877         })
1878
1879         lastFunction := -1
1880         var errbuf bytes.Buffer
1881         fmt.Fprintln(&errbuf)
1882         for _, o := range failed {
1883                 // Dump the function in which this opcode check was supposed to
1884                 // pass but failed.
1885                 funcIdx := lineFuncMap[o.fileline]
1886                 if funcIdx != 0 && funcIdx != lastFunction {
1887                         funcLines := lines[functionMarkers[funcIdx]:functionMarkers[funcIdx+1]]
1888                         log.Println(strings.Join(funcLines, "\n"))
1889                         lastFunction = funcIdx // avoid printing same function twice
1890                 }
1891
1892                 if o.negative {
1893                         fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
1894                 } else {
1895                         fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
1896                 }
1897         }
1898         err = errors.New(errbuf.String())
1899         return
1900 }
1901
1902 // defaultRunOutputLimit returns the number of runoutput tests that
1903 // can be executed in parallel.
1904 func defaultRunOutputLimit() int {
1905         const maxArmCPU = 2
1906
1907         cpu := runtime.NumCPU()
1908         if runtime.GOARCH == "arm" && cpu > maxArmCPU {
1909                 cpu = maxArmCPU
1910         }
1911         return cpu
1912 }
1913
1914 // checkShouldTest runs sanity checks on the shouldTest function.
1915 func checkShouldTest() {
1916         assert := func(ok bool, _ string) {
1917                 if !ok {
1918                         panic("fail")
1919                 }
1920         }
1921         assertNot := func(ok bool, _ string) { assert(!ok, "") }
1922
1923         // Simple tests.
1924         assert(shouldTest("// +build linux", "linux", "arm"))
1925         assert(shouldTest("// +build !windows", "linux", "arm"))
1926         assertNot(shouldTest("// +build !windows", "windows", "amd64"))
1927
1928         // A file with no build tags will always be tested.
1929         assert(shouldTest("// This is a test.", "os", "arch"))
1930
1931         // Build tags separated by a space are OR-ed together.
1932         assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
1933
1934         // Build tags separated by a comma are AND-ed together.
1935         assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
1936         assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
1937
1938         // Build tags on multiple lines are AND-ed together.
1939         assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
1940         assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
1941
1942         // Test that (!a OR !b) matches anything.
1943         assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
1944 }
1945
1946 func getenv(key, def string) string {
1947         value := os.Getenv(key)
1948         if value != "" {
1949                 return value
1950         }
1951         return def
1952 }
1953
1954 // overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
1955 func overlayDir(dstRoot, srcRoot string) error {
1956         dstRoot = filepath.Clean(dstRoot)
1957         if err := os.MkdirAll(dstRoot, 0777); err != nil {
1958                 return err
1959         }
1960
1961         srcRoot, err := filepath.Abs(srcRoot)
1962         if err != nil {
1963                 return err
1964         }
1965
1966         return filepath.WalkDir(srcRoot, func(srcPath string, d fs.DirEntry, err error) error {
1967                 if err != nil || srcPath == srcRoot {
1968                         return err
1969                 }
1970
1971                 suffix := strings.TrimPrefix(srcPath, srcRoot)
1972                 for len(suffix) > 0 && suffix[0] == filepath.Separator {
1973                         suffix = suffix[1:]
1974                 }
1975                 dstPath := filepath.Join(dstRoot, suffix)
1976
1977                 var info fs.FileInfo
1978                 if d.Type()&os.ModeSymlink != 0 {
1979                         info, err = os.Stat(srcPath)
1980                 } else {
1981                         info, err = d.Info()
1982                 }
1983                 if err != nil {
1984                         return err
1985                 }
1986                 perm := info.Mode() & os.ModePerm
1987
1988                 // Always copy directories (don't symlink them).
1989                 // If we add a file in the overlay, we don't want to add it in the original.
1990                 if info.IsDir() {
1991                         return os.MkdirAll(dstPath, perm|0200)
1992                 }
1993
1994                 // If the OS supports symlinks, use them instead of copying bytes.
1995                 if err := os.Symlink(srcPath, dstPath); err == nil {
1996                         return nil
1997                 }
1998
1999                 // Otherwise, copy the bytes.
2000                 src, err := os.Open(srcPath)
2001                 if err != nil {
2002                         return err
2003                 }
2004                 defer src.Close()
2005
2006                 dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
2007                 if err != nil {
2008                         return err
2009                 }
2010
2011                 _, err = io.Copy(dst, src)
2012                 if closeErr := dst.Close(); err == nil {
2013                         err = closeErr
2014                 }
2015                 return err
2016         })
2017 }
2018
2019 // The following is temporary scaffolding to get types2 typechecker
2020 // up and running against the existing test cases. The explicitly
2021 // listed files don't pass yet, usually because the error messages
2022 // are slightly different (this list is not complete). Any errorcheck
2023 // tests that require output from analysis phases past initial type-
2024 // checking are also excluded since these phases are not running yet.
2025 // We can get rid of this code once types2 is fully plugged in.
2026
2027 // List of files that the compiler cannot errorcheck with the new typechecker (compiler -G option).
2028 // Temporary scaffolding until we pass all the tests at which point this map can be removed.
2029 var excludedFiles = map[string]bool{
2030         "directive.go":    true, // misplaced compiler directive checks
2031         "float_lit3.go":   true, // types2 reports extra errors
2032         "import1.go":      true, // types2 reports extra errors
2033         "import6.go":      true, // issue #43109
2034         "initializerr.go": true, // types2 reports extra errors
2035         "linkname2.go":    true, // error reported by noder (not running for types2 errorcheck test)
2036         "notinheap.go":    true, // types2 doesn't report errors about conversions that are invalid due to //go:notinheap
2037         "printbig.go":     true, // large untyped int passed to print (32-bit)
2038         "shift1.go":       true, // issue #42989
2039         "typecheck.go":    true, // invalid function is not causing errors when called
2040         "writebarrier.go": true, // correct diagnostics, but different lines (probably irgen's fault)
2041
2042         "interface/private.go": true, // types2 phrases errors differently (doesn't use non-spec "private" term)
2043
2044         "fixedbugs/bug114.go":    true, // large untyped int passed to println (32-bit)
2045         "fixedbugs/bug176.go":    true, // types2 reports all errors (pref: types2)
2046         "fixedbugs/bug195.go":    true, // types2 reports slightly different (but correct) bugs
2047         "fixedbugs/bug228.go":    true, // types2 doesn't run when there are syntax errors
2048         "fixedbugs/bug231.go":    true, // types2 bug? (same error reported twice)
2049         "fixedbugs/bug255.go":    true, // types2 reports extra errors
2050         "fixedbugs/bug374.go":    true, // types2 reports extra errors
2051         "fixedbugs/bug385_32.go": true, // types2 doesn't produce missing error "type .* too large" (32-bit specific)
2052         "fixedbugs/bug388.go":    true, // types2 not run due to syntax errors
2053         "fixedbugs/bug412.go":    true, // types2 produces a follow-on error
2054
2055         "fixedbugs/issue10700.go":  true, // types2 reports ok hint, but does not match regexp
2056         "fixedbugs/issue11590.go":  true, // types2 doesn't report a follow-on error (pref: types2)
2057         "fixedbugs/issue11610.go":  true, // types2 not run after syntax errors
2058         "fixedbugs/issue11614.go":  true, // types2 reports an extra error
2059         "fixedbugs/issue14520.go":  true, // missing import path error by types2
2060         "fixedbugs/issue16133.go":  true, // types2 doesn't use package path for qualified identifiers when package name is ambiguous
2061         "fixedbugs/issue16428.go":  true, // types2 reports two instead of one error
2062         "fixedbugs/issue17038.go":  true, // types2 doesn't report a follow-on error (pref: types2)
2063         "fixedbugs/issue17270.go":  true, // ICE in irgen
2064         "fixedbugs/issue17645.go":  true, // multiple errors on same line
2065         "fixedbugs/issue18331.go":  true, // missing error about misuse of //go:noescape (irgen needs code from noder)
2066         "fixedbugs/issue18419.go":  true, // types2 reports
2067         "fixedbugs/issue19012.go":  true, // multiple errors on same line
2068         "fixedbugs/issue20174.go":  true, // ICE due to width not calculated (probably irgen's fault)
2069         "fixedbugs/issue20233.go":  true, // types2 reports two instead of one error (pref: compiler)
2070         "fixedbugs/issue20245.go":  true, // types2 reports two instead of one error (pref: compiler)
2071         "fixedbugs/issue20250.go":  true, // correct diagnostics, but different lines (probably irgen's fault)
2072         "fixedbugs/issue21979.go":  true, // types2 doesn't report a follow-on error (pref: types2)
2073         "fixedbugs/issue23305.go":  true, // large untyped int passed to println (32-bit)
2074         "fixedbugs/issue23732.go":  true, // types2 reports different (but ok) line numbers
2075         "fixedbugs/issue25958.go":  true, // types2 doesn't report a follow-on error (pref: types2)
2076         "fixedbugs/issue28079b.go": true, // types2 reports follow-on errors
2077         "fixedbugs/issue28268.go":  true, // types2 reports follow-on errors
2078         "fixedbugs/issue31053.go":  true, // types2 reports "unknown field" instead of "cannot refer to unexported field"
2079         "fixedbugs/issue33460.go":  true, // types2 reports alternative positions in separate error
2080         "fixedbugs/issue42058a.go": true, // types2 doesn't report "channel element type too large"
2081         "fixedbugs/issue42058b.go": true, // types2 doesn't report "channel element type too large"
2082         "fixedbugs/issue46725.go":  true, // fix applied to typecheck needs to be ported to irgen/transform
2083         "fixedbugs/issue4232.go":   true, // types2 reports (correct) extra errors
2084         "fixedbugs/issue4452.go":   true, // types2 reports (correct) extra errors
2085         "fixedbugs/issue4510.go":   true, // types2 reports different (but ok) line numbers
2086         "fixedbugs/issue5609.go":   true, // types2 needs a better error message
2087         "fixedbugs/issue7525b.go":  true, // types2 reports init cycle error on different line - ok otherwise
2088         "fixedbugs/issue7525c.go":  true, // types2 reports init cycle error on different line - ok otherwise
2089         "fixedbugs/issue7525d.go":  true, // types2 reports init cycle error on different line - ok otherwise
2090         "fixedbugs/issue7525e.go":  true, // types2 reports init cycle error on different line - ok otherwise
2091         "fixedbugs/issue7525.go":   true, // types2 reports init cycle error on different line - ok otherwise
2092         "fixedbugs/issue46749.go":  true, // types2 reports can not convert error instead of type mismatched
2093         "fixedbugs/issue9691.go":   true, // "cannot assign to int(.autotmp_4)" (probably irgen's fault)
2094
2095         // tests that rely on -m diagnostics, which currently differ with -G=3
2096         //
2097         // TODO(mdempsky): Triage, though most of the issues seem to fall into:
2098         // - Anonymous result parameters given different names (e.g., ~r0 vs ~r1)
2099         // - Some escape analysis diagnostics being printed without position information
2100         // - Some expressions printed differently (e.g., "int(100)" instead
2101         //   of "100" or "&composite literal" instead of "&[4]int{...}").
2102         "closure3.go":             true,
2103         "escape2.go":              true,
2104         "escape2n.go":             true,
2105         "escape4.go":              true,
2106         "escape_calls.go":         true,
2107         "escape_field.go":         true,
2108         "escape_iface.go":         true,
2109         "escape_indir.go":         true,
2110         "escape_level.go":         true,
2111         "escape_map.go":           true,
2112         "escape_param.go":         true,
2113         "escape_slice.go":         true,
2114         "escape_struct_param1.go": true,
2115         "escape_struct_param2.go": true,
2116         "fixedbugs/issue12006.go": true,
2117         "fixedbugs/issue13799.go": true,
2118         "fixedbugs/issue21709.go": true,
2119         "fixedbugs/issue31573.go": true,
2120         "fixedbugs/issue37837.go": true,
2121         "fixedbugs/issue39292.go": true,
2122         "fixedbugs/issue7921.go":  true,
2123         "inline.go":               true,
2124 }
2125
2126 // splitQuoted splits the string s around each instance of one or more consecutive
2127 // white space characters while taking into account quotes and escaping, and
2128 // returns an array of substrings of s or an empty list if s contains only white space.
2129 // Single quotes and double quotes are recognized to prevent splitting within the
2130 // quoted region, and are removed from the resulting substrings. If a quote in s
2131 // isn't closed err will be set and r will have the unclosed argument as the
2132 // last element. The backslash is used for escaping.
2133 //
2134 // For example, the following string:
2135 //
2136 //     a b:"c d" 'e''f'  "g\""
2137 //
2138 // Would be parsed as:
2139 //
2140 //     []string{"a", "b:c d", "ef", `g"`}
2141 //
2142 // [copied from src/go/build/build.go]
2143 func splitQuoted(s string) (r []string, err error) {
2144         var args []string
2145         arg := make([]rune, len(s))
2146         escaped := false
2147         quoted := false
2148         quote := '\x00'
2149         i := 0
2150         for _, rune := range s {
2151                 switch {
2152                 case escaped:
2153                         escaped = false
2154                 case rune == '\\':
2155                         escaped = true
2156                         continue
2157                 case quote != '\x00':
2158                         if rune == quote {
2159                                 quote = '\x00'
2160                                 continue
2161                         }
2162                 case rune == '"' || rune == '\'':
2163                         quoted = true
2164                         quote = rune
2165                         continue
2166                 case unicode.IsSpace(rune):
2167                         if quoted || i > 0 {
2168                                 quoted = false
2169                                 args = append(args, string(arg[:i]))
2170                                 i = 0
2171                         }
2172                         continue
2173                 }
2174                 arg[i] = rune
2175                 i++
2176         }
2177         if quoted || i > 0 {
2178                 args = append(args, string(arg[:i]))
2179         }
2180         if quote != 0 {
2181                 err = errors.New("unclosed quote")
2182         } else if escaped {
2183                 err = errors.New("unfinished escaping")
2184         }
2185         return args, err
2186 }