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