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