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

View as plain text