blob: b2902f190c357e1158bf3fa8083472db5f39617e [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001// skip
2
Dan Willemsen0c157092016-07-08 13:57:52 -07003// Copyright 2012 The Go Authors. All rights reserved.
Colin Cross7bb052a2015-02-03 12:59:37 -08004// 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.
Colin Cross7bb052a2015-02-03 12:59:37 -08008package main
9
10import (
11 "bytes"
Dan Willemsen59ee7802021-12-15 01:08:25 -080012 "encoding/json"
Colin Cross7bb052a2015-02-03 12:59:37 -080013 "errors"
14 "flag"
15 "fmt"
Dan Willemsen31b9b842021-08-31 12:51:40 -070016 "go/build"
Dan Willemsen1cbb82a2022-08-08 23:13:50 -070017 "go/build/constraint"
Dan Willemsen6ff23252015-09-15 13:49:18 -070018 "hash/fnv"
19 "io"
Colin Cross846c3162021-05-14 11:11:40 -070020 "io/fs"
Colin Cross7bb052a2015-02-03 12:59:37 -080021 "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
36var (
37 verbose = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.")
Dan Willemsen0c157092016-07-08 13:57:52 -070038 keep = flag.Bool("k", false, "keep. keep temporary directory.")
Colin Cross7bb052a2015-02-03 12:59:37 -080039 numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run")
40 summary = flag.Bool("summary", false, "show summary of results")
Patrice Arruda7f4776e2020-06-25 11:55:41 -070041 allCodegen = flag.Bool("all_codegen", defaultAllCodeGen(), "run all goos/goarch for codegen")
Colin Cross7bb052a2015-02-03 12:59:37 -080042 showSkips = flag.Bool("show_skips", false, "show skipped tests")
Dan Willemsen0c157092016-07-08 13:57:52 -070043 runSkips = flag.Bool("run_skips", false, "run skipped tests (ignore skip and build tags)")
44 linkshared = flag.Bool("linkshared", false, "")
Dan Willemsen6ff23252015-09-15 13:49:18 -070045 updateErrors = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
Colin Cross7bb052a2015-02-03 12:59:37 -080046 runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
Dan Willemsen59ee7802021-12-15 01:08:25 -080047 force = flag.Bool("f", false, "ignore expected-failure test lists")
Dan Willemsen6ff23252015-09-15 13:49:18 -070048
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.")
Colin Cross7bb052a2015-02-03 12:59:37 -080051)
52
Dan Willemsen59ee7802021-12-15 01:08:25 -080053type envVars struct {
54 GOOS string
55 GOARCH string
56 GOEXPERIMENT string
57 CGO_ENABLED string
58}
59
60var env = func() (res envVars) {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -070061 cmd := exec.Command(goTool(), "env", "-json")
Dan Willemsen59ee7802021-12-15 01:08:25 -080062 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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -070078// 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.
81var unifiedEnabled = strings.Contains(","+env.GOEXPERIMENT+",", ",unified,")
Dan Willemsen59ee7802021-12-15 01:08:25 -080082
Patrice Arruda7f4776e2020-06-25 11:55:41 -070083// 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.
87func defaultAllCodeGen() bool {
88 return os.Getenv("GO_BUILDER_NAME") == "linux-amd64"
89}
90
Colin Cross7bb052a2015-02-03 12:59:37 -080091var (
Dan Willemsen59ee7802021-12-15 01:08:25 -080092 goos = env.GOOS
93 goarch = env.GOARCH
94 cgoEnabled, _ = strconv.ParseBool(env.CGO_ENABLED)
Colin Cross7bb052a2015-02-03 12:59:37 -080095
96 // dirs are the directories to look for *.go files in.
97 // TODO(bradfitz): just use all directories?
Dan Willemsen59ee7802021-12-15 01:08:25 -080098 dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky"}
Colin Cross7bb052a2015-02-03 12:59:37 -080099
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.
114const maxTests = 5000
115
116func main() {
117 flag.Parse()
118
Colin Cross7bb052a2015-02-03 12:59:37 -0800119 findExecCmd()
120
121 // Disable parallelism if printing or if using a simulator.
122 if *verbose || len(findExecCmd()) > 0 {
123 *numParallel = 1
Colin Cross1371fe42019-03-19 21:08:48 -0700124 *runoutputLimit = 1
Colin Cross7bb052a2015-02-03 12:59:37 -0800125 }
126
127 ratec = make(chan bool, *numParallel)
128 rungatec = make(chan bool, *runoutputLimit)
Colin Cross7bb052a2015-02-03 12:59:37 -0800129
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) {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700143 tests = append(tests, startTest(arg, baseGoFile))
Colin Cross7bb052a2015-02-03 12:59:37 -0800144 }
145 } else if strings.HasSuffix(arg, ".go") {
146 dir, file := filepath.Split(arg)
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700147 tests = append(tests, startTest(dir, file))
Colin Cross7bb052a2015-02-03 12:59:37 -0800148 } 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) {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700155 tests = append(tests, startTest(dir, baseGoFile))
Colin Cross7bb052a2015-02-03 12:59:37 -0800156 }
157 }
158 }
159
160 failed := false
161 resCount := map[string]int{}
162 for _, test := range tests {
163 <-test.donec
164 status := "ok "
165 errStr := ""
Dan Willemsen0c157092016-07-08 13:57:52 -0700166 if e, isSkip := test.err.(skipError); isSkip {
Colin Cross7bb052a2015-02-03 12:59:37 -0800167 test.err = nil
Dan Willemsen0c157092016-07-08 13:57:52 -0700168 errStr = "unexpected skip for " + path.Join(test.dir, test.gofile) + ": " + string(e)
Dan Willemsen6ff23252015-09-15 13:49:18 -0700169 status = "FAIL"
Colin Cross7bb052a2015-02-03 12:59:37 -0800170 }
171 if test.err != nil {
Colin Cross7bb052a2015-02-03 12:59:37 -0800172 errStr = test.err.Error()
Dan Willemsen59ee7802021-12-15 01:08:25 -0800173 if test.expectFail {
174 errStr += " (expected)"
175 } else {
176 status = "FAIL"
177 }
178 } else if test.expectFail {
179 status = "FAIL"
180 errStr = "unexpected success"
Colin Cross7bb052a2015-02-03 12:59:37 -0800181 }
182 if status == "FAIL" {
183 failed = true
184 }
185 resCount[status]++
Colin Cross7bb052a2015-02-03 12:59:37 -0800186 dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
187 if status == "FAIL" {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700188 fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n",
Colin Cross7bb052a2015-02-03 12:59:37 -0800189 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
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700210// 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.
213func 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
Dan Willemsen6ff23252015-09-15 13:49:18 -0700226func 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
Colin Cross7bb052a2015-02-03 12:59:37 -0800235func goFiles(dir string) []string {
236 f, err := os.Open(dir)
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700237 if err != nil {
238 log.Fatal(err)
239 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800240 dirnames, err := f.Readdirnames(-1)
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700241 f.Close()
242 if err != nil {
243 log.Fatal(err)
244 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800245 names := []string{}
246 for _, name := range dirnames {
Dan Willemsen6ff23252015-09-15 13:49:18 -0700247 if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) {
Colin Cross7bb052a2015-02-03 12:59:37 -0800248 names = append(names, name)
249 }
250 }
251 sort.Strings(names)
252 return names
253}
254
255type runCmd func(...string) ([]byte, error)
256
Dan Willemsenc78f7142017-07-26 13:08:14 -0700257func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700258 cmd := []string{goTool(), "tool", "compile", "-e", "-p=p"}
Dan Willemsenc78f7142017-07-26 13:08:14 -0700259 cmd = append(cmd, flags...)
Dan Willemsen0c157092016-07-08 13:57:52 -0700260 if *linkshared {
261 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
262 }
263 cmd = append(cmd, longname)
264 return runcmd(cmd...)
Colin Cross7bb052a2015-02-03 12:59:37 -0800265}
266
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700267func 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)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700274 }
Dan Willemsen0c157092016-07-08 13:57:52 -0700275 cmd = append(cmd, flags...)
276 if *linkshared {
277 cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
278 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800279 for _, name := range names {
280 cmd = append(cmd, filepath.Join(dir, name))
281 }
282 return runcmd(cmd...)
283}
284
Colin Crossefed6342019-09-07 08:34:44 -0700285func linkFile(runcmd runCmd, goname string, ldflags []string) (err error) {
Dan Willemsen6ff23252015-09-15 13:49:18 -0700286 pfile := strings.Replace(goname, ".go", ".o", -1)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700287 cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."}
Dan Willemsen0c157092016-07-08 13:57:52 -0700288 if *linkshared {
289 cmd = append(cmd, "-linkshared", "-installsuffix=dynlink")
290 }
Colin Crossefed6342019-09-07 08:34:44 -0700291 if ldflags != nil {
292 cmd = append(cmd, ldflags...)
293 }
Dan Willemsen0c157092016-07-08 13:57:52 -0700294 cmd = append(cmd, pfile)
295 _, err = runcmd(cmd...)
Colin Cross7bb052a2015-02-03 12:59:37 -0800296 return
297}
298
299// skipError describes why a test was skipped.
300type skipError string
301
302func (s skipError) Error() string { return string(s) }
303
Colin Cross7bb052a2015-02-03 12:59:37 -0800304// test holds the state of a test.
305type test struct {
306 dir, gofile string
307 donec chan bool // closed when done
308 dt time.Duration
309
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800310 src string
Colin Cross7bb052a2015-02-03 12:59:37 -0800311
312 tempDir string
313 err error
Dan Willemsen59ee7802021-12-15 01:08:25 -0800314
315 // expectFail indicates whether the (overall) test recipe is
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700316 // expected to fail under the current test configuration (e.g.,
317 // GOEXPERIMENT=unified).
Dan Willemsen59ee7802021-12-15 01:08:25 -0800318 expectFail bool
Colin Cross7bb052a2015-02-03 12:59:37 -0800319}
320
Dan Willemsen59ee7802021-12-15 01:08:25 -0800321// initExpectFail initializes t.expectFail based on the build+test
322// configuration.
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700323func (t *test) initExpectFail() {
Dan Willemsen59ee7802021-12-15 01:08:25 -0800324 if *force {
325 return
Colin Cross7bb052a2015-02-03 12:59:37 -0800326 }
Dan Willemsen59ee7802021-12-15 01:08:25 -0800327
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700328 failureSets := []map[string]bool{types2Failures}
Dan Willemsen14b5f992022-03-10 14:27:21 -0800329
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700330 // 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)
Dan Willemsen59ee7802021-12-15 01:08:25 -0800339 } else {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700340 failureSets = append(failureSets, go118Failures)
Dan Willemsen59ee7802021-12-15 01:08:25 -0800341 }
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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700353func startTest(dir, gofile string) *test {
354 t := &test{
355 dir: dir,
356 gofile: gofile,
357 donec: make(chan bool, 1),
Dan Willemsen59ee7802021-12-15 01:08:25 -0800358 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700359 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
Colin Cross7bb052a2015-02-03 12:59:37 -0800369}
370
371// runTests runs tests in parallel, but respecting the order they
372// were enqueued on the toRun channel.
373func runTests() {
374 for {
375 ratec <- true
376 t := <-toRun
377 go func() {
378 t.run()
379 <-ratec
380 }()
381 }
382}
383
384var cwd, _ = os.Getwd()
385
386func (t *test) goFileName() string {
387 return filepath.Join(t.dir, t.gofile)
388}
389
390func (t *test) goDirName() string {
391 return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1))
392}
393
394func 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
Colin Cross1371fe42019-03-19 21:08:48 -0700407var packageRE = regexp.MustCompile(`(?m)^package ([\p{Lu}\p{Ll}\w]+)`)
Colin Cross7bb052a2015-02-03 12:59:37 -0800408
Colin Crossefed6342019-09-07 08:34:44 -0700409func 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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700421type goDirPkg struct {
422 name string
423 files []string
424}
425
Dan Willemsen0c157092016-07-08 13:57:52 -0700426// If singlefilepkgs is set, each file is considered a separate package
427// even if the package names are the same.
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700428func goDirPackages(longdir string, singlefilepkgs bool) ([]*goDirPkg, error) {
Colin Cross7bb052a2015-02-03 12:59:37 -0800429 files, err := goDirFiles(longdir)
430 if err != nil {
431 return nil, err
432 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700433 var pkgs []*goDirPkg
434 m := make(map[string]*goDirPkg)
Colin Cross7bb052a2015-02-03 12:59:37 -0800435 for _, file := range files {
436 name := file.Name()
Colin Crossefed6342019-09-07 08:34:44 -0700437 pkgname, err := getPackageNameFromSource(filepath.Join(longdir, name))
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700438 if err != nil {
439 log.Fatal(err)
440 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700441 p, ok := m[pkgname]
Dan Willemsen0c157092016-07-08 13:57:52 -0700442 if singlefilepkgs || !ok {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700443 p = &goDirPkg{name: pkgname}
444 pkgs = append(pkgs, p)
445 m[pkgname] = p
Colin Cross7bb052a2015-02-03 12:59:37 -0800446 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700447 p.files = append(p.files, name)
Colin Cross7bb052a2015-02-03 12:59:37 -0800448 }
449 return pkgs, nil
450}
451
452type context struct {
Dan Willemsen31b9b842021-08-31 12:51:40 -0700453 GOOS string
454 GOARCH string
455 cgoEnabled bool
456 noOptEnv bool
Colin Cross7bb052a2015-02-03 12:59:37 -0800457}
458
459// shouldTest looks for build tags in a source file and returns
460// whether the file should be used according to the tags.
461func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
Dan Willemsen0c157092016-07-08 13:57:52 -0700462 if *runSkips {
463 return true, ""
464 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800465 for _, line := range strings.Split(src, "\n") {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700466 if strings.HasPrefix(line, "package ") {
467 break
Colin Cross7bb052a2015-02-03 12:59:37 -0800468 }
Colin Cross1371fe42019-03-19 21:08:48 -0700469
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700470 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"),
Colin Cross7bb052a2015-02-03 12:59:37 -0800477 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700478
479 if !expr.Eval(ctxt.match) {
Colin Cross7bb052a2015-02-03 12:59:37 -0800480 return false, line
481 }
482 }
483 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800484 return true, ""
485}
486
487func (ctxt *context) match(name string) bool {
488 if name == "" {
489 return false
490 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800491
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
Dan Willemsen31b9b842021-08-31 12:51:40 -0700500 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
Colin Cross846c3162021-05-14 11:11:40 -0700513 if name == ctxt.GOOS || name == ctxt.GOARCH || name == "gc" {
Colin Cross7bb052a2015-02-03 12:59:37 -0800514 return true
515 }
516
Colin Cross1371fe42019-03-19 21:08:48 -0700517 if ctxt.noOptEnv && name == "gcflags_noopt" {
518 return true
519 }
520
Dan Willemsen0c157092016-07-08 13:57:52 -0700521 if name == "test_run" {
522 return true
523 }
524
Colin Cross7bb052a2015-02-03 12:59:37 -0800525 return false
526}
527
528func init() { checkShouldTest() }
529
Dan Willemsene1b3b182018-02-27 19:36:27 -0800530// goGcflags returns the -gcflags argument to use with go build / go run.
Colin Cross1371fe42019-03-19 21:08:48 -0700531// This must match the flags used for building the standard library,
Dan Willemsene1b3b182018-02-27 19:36:27 -0800532// or else the commands will rebuild any needed packages (like runtime)
533// over and over.
Dan Willemsen59ee7802021-12-15 01:08:25 -0800534func (t *test) goGcflags() string {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700535 return "-gcflags=all=" + os.Getenv("GO_GCFLAGS")
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700536}
537
Dan Willemsen59ee7802021-12-15 01:08:25 -0800538func (t *test) goGcflagsIsEmpty() bool {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700539 return "" == os.Getenv("GO_GCFLAGS")
Dan Willemsene1b3b182018-02-27 19:36:27 -0800540}
541
Colin Cross846c3162021-05-14 11:11:40 -0700542var errTimeout = errors.New("command exceeded time limit")
543
Colin Cross7bb052a2015-02-03 12:59:37 -0800544// run runs a test.
545func (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 }
Dan Willemsen6ff23252015-09-15 13:49:18 -0700562
563 // Execution recipe stops at first blank line.
Dan Willemsen59ee7802021-12-15 01:08:25 -0800564 action, _, ok := strings.Cut(t.src, "\n\n")
565 if !ok {
Dan Willemsen31b9b842021-08-31 12:51:40 -0700566 t.err = fmt.Errorf("double newline ending execution recipe not found in %s", t.goFileName())
Colin Cross7bb052a2015-02-03 12:59:37 -0800567 return
568 }
Dan Willemsen59ee7802021-12-15 01:08:25 -0800569 if firstLine, rest, ok := strings.Cut(action, "\n"); ok && strings.Contains(firstLine, "+build") {
Colin Cross7bb052a2015-02-03 12:59:37 -0800570 // skip first line
Dan Willemsen59ee7802021-12-15 01:08:25 -0800571 action = rest
Colin Cross7bb052a2015-02-03 12:59:37 -0800572 }
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700573 action = strings.TrimPrefix(action, "//")
Colin Cross7bb052a2015-02-03 12:59:37 -0800574
Dan Willemsen6ff23252015-09-15 13:49:18 -0700575 // Check for build constraints only up to the actual code.
Dan Willemsen59ee7802021-12-15 01:08:25 -0800576 header, _, ok := strings.Cut(t.src, "\npackage")
577 if !ok {
578 header = action // some files are intentionally malformed
Dan Willemsen6ff23252015-09-15 13:49:18 -0700579 }
Dan Willemsen59ee7802021-12-15 01:08:25 -0800580 if ok, why := shouldTest(header, goos, goarch); !ok {
Dan Willemsen6ff23252015-09-15 13:49:18 -0700581 if *showSkips {
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800582 fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why)
Dan Willemsen6ff23252015-09-15 13:49:18 -0700583 }
584 return
585 }
586
Dan Willemsen31b9b842021-08-31 12:51:40 -0700587 var args, flags, runenv []string
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800588 var tim int
Colin Cross7bb052a2015-02-03 12:59:37 -0800589 wantError := false
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800590 wantAuto := false
Dan Willemsen0c157092016-07-08 13:57:52 -0700591 singlefilepkgs := false
Dan Willemsen59ee7802021-12-15 01:08:25 -0800592 f, err := splitQuoted(action)
593 if err != nil {
594 t.err = fmt.Errorf("invalid test recipe: %v", err)
595 return
596 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800597 if len(f) > 0 {
598 action = f[0]
599 args = f[1:]
600 }
601
Dan Willemsen0c157092016-07-08 13:57:52 -0700602 // TODO: Clean up/simplify this switch statement.
Colin Cross7bb052a2015-02-03 12:59:37 -0800603 switch action {
Colin Crossefed6342019-09-07 08:34:44 -0700604 case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "runindir", "asmcheck":
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800605 // nothing to do
Dan Willemsen0c157092016-07-08 13:57:52 -0700606 case "errorcheckandrundir":
607 wantError = false // should be no error if also will run
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800608 case "errorcheckwithauto":
609 action = "errorcheck"
610 wantAuto = true
611 wantError = true
Colin Cross7bb052a2015-02-03 12:59:37 -0800612 case "errorcheck", "errorcheckdir", "errorcheckoutput":
Colin Cross7bb052a2015-02-03 12:59:37 -0800613 wantError = true
Colin Cross7bb052a2015-02-03 12:59:37 -0800614 case "skip":
Dan Willemsen0c157092016-07-08 13:57:52 -0700615 if *runSkips {
616 break
617 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800618 return
619 default:
620 t.err = skipError("skipped; unknown pattern: " + action)
Colin Cross7bb052a2015-02-03 12:59:37 -0800621 return
622 }
623
Dan Willemsen59ee7802021-12-15 01:08:25 -0800624 goexp := env.GOEXPERIMENT
625
Dan Willemsen0c157092016-07-08 13:57:52 -0700626 // collect flags
627 for len(args) > 0 && strings.HasPrefix(args[0], "-") {
628 switch args[0] {
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700629 case "-1":
630 wantError = true
Dan Willemsen0c157092016-07-08 13:57:52 -0700631 case "-0":
632 wantError = false
633 case "-s":
634 singlefilepkgs = true
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800635 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 }
Dan Willemsen14b5f992022-03-10 14:27:21 -0800642 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 }
Dan Willemsen31b9b842021-08-31 12:51:40 -0700649 case "-goexperiment": // set GOEXPERIMENT environment
650 args = args[1:]
Dan Willemsen59ee7802021-12-15 01:08:25 -0800651 if goexp != "" {
652 goexp += ","
653 }
654 goexp += args[0]
655 runenv = append(runenv, "GOEXPERIMENT="+goexp)
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800656
Dan Willemsen0c157092016-07-08 13:57:52 -0700657 default:
658 flags = append(flags, args[0])
659 }
660 args = args[1:]
661 }
Colin Cross1371fe42019-03-19 21:08:48 -0700662 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 }
Dan Willemsen0c157092016-07-08 13:57:52 -0700675
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700676 t.initExpectFail()
Colin Cross7bb052a2015-02-03 12:59:37 -0800677 t.makeTempDir()
Dan Willemsen0c157092016-07-08 13:57:52 -0700678 if !*keep {
679 defer os.RemoveAll(t.tempDir)
680 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800681
682 err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700683 if err != nil {
684 log.Fatal(err)
685 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800686
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
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700695 var (
696 runInDir = t.tempDir
697 tempDirIsGOPATH = false
698 )
Colin Cross7bb052a2015-02-03 12:59:37 -0800699 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
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700704 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)
Colin Crossefed6342019-09-07 08:34:44 -0700709 }
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700710 if tempDirIsGOPATH {
711 cmd.Env = append(cmd.Env, "GOPATH="+t.tempDir)
Dan Willemsen0c157092016-07-08 13:57:52 -0700712 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700713 // 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
Dan Willemsen31b9b842021-08-31 12:51:40 -0700729 cmd.Env = append(cmd.Env, runenv...)
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800730
731 var err error
732
733 if tim != 0 {
734 err = cmd.Start()
735 // This command-timeout code adapted from cmd/go/test.go
Dan Willemsen14b5f992022-03-10 14:27:21 -0800736 // 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.
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800743 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:
Colin Cross846c3162021-05-14 11:11:40 -0700753 cmd.Process.Signal(os.Interrupt)
754 time.Sleep(1 * time.Second)
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800755 cmd.Process.Kill()
Colin Cross846c3162021-05-14 11:11:40 -0700756 <-done
757 err = errTimeout
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800758 }
759 tick.Stop()
760 }
761 } else {
762 err = cmd.Run()
Colin Cross7bb052a2015-02-03 12:59:37 -0800763 }
Colin Cross846c3162021-05-14 11:11:40 -0700764 if err != nil && err != errTimeout {
Colin Cross7bb052a2015-02-03 12:59:37 -0800765 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
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700775 case "asmcheck":
776 // Compile Go file and match the generated assembly
777 // against a set of regexps in comments.
778 ops := t.wantedAsmOpcodes(long)
Colin Crossefed6342019-09-07 08:34:44 -0700779 self := runtime.GOOS + "/" + runtime.GOARCH
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700780 for _, env := range ops.Envs() {
Colin Crossefed6342019-09-07 08:34:44 -0700781 // 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 }
Colin Cross1371fe42019-03-19 21:08:48 -0700786 // -S=2 forces outermost line numbers when disassembling inlined code.
787 cmdline := []string{"build", "-gcflags", "-S=2"}
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700788
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
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700807 cmdline = append(cmdline, long)
808 cmd := exec.Command(goTool(), cmdline...)
809 cmd.Env = append(os.Environ(), env.Environ()...)
Colin Crossefed6342019-09-07 08:34:44 -0700810 if len(flags) > 0 && flags[0] == "-race" {
811 cmd.Env = append(cmd.Env, "CGO_ENABLED=1")
812 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700813
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
Colin Cross7bb052a2015-02-03 12:59:37 -0800829 case "errorcheck":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700830 // 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.
Dan Willemsenc78f7142017-07-26 13:08:14 -0700833 // TODO(gri) remove need for -C (disable printing of columns in error messages)
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700834 cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-C", "-e", "-o", "a.o"}
Dan Willemsen0c157092016-07-08 13:57:52 -0700835 // No need to add -dynlink even if linkshared if we're just checking for errors...
Colin Cross7bb052a2015-02-03 12:59:37 -0800836 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 }
Colin Cross846c3162021-05-14 11:11:40 -0700844 if err == errTimeout {
845 t.err = fmt.Errorf("compilation timed out")
846 return
847 }
Colin Cross7bb052a2015-02-03 12:59:37 -0800848 } else {
849 if err != nil {
850 t.err = err
851 return
852 }
853 }
Dan Willemsen6ff23252015-09-15 13:49:18 -0700854 if *updateErrors {
855 t.updateErrors(string(out), long)
856 }
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800857 t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
Colin Cross7bb052a2015-02-03 12:59:37 -0800858
859 case "compile":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700860 // Compile Go file.
Dan Willemsenc78f7142017-07-26 13:08:14 -0700861 _, t.err = compileFile(runcmd, long, flags)
Colin Cross7bb052a2015-02-03 12:59:37 -0800862
863 case "compiledir":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700864 // Compile all files in the directory as packages in lexicographic order.
Colin Cross7bb052a2015-02-03 12:59:37 -0800865 longdir := filepath.Join(cwd, t.goDirName())
Dan Willemsen0c157092016-07-08 13:57:52 -0700866 pkgs, err := goDirPackages(longdir, singlefilepkgs)
Colin Cross7bb052a2015-02-03 12:59:37 -0800867 if err != nil {
868 t.err = err
869 return
870 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700871 for _, pkg := range pkgs {
872 _, t.err = compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...)
Colin Cross7bb052a2015-02-03 12:59:37 -0800873 if t.err != nil {
874 return
875 }
876 }
877
Dan Willemsen0c157092016-07-08 13:57:52 -0700878 case "errorcheckdir", "errorcheckandrundir":
Dan Willemsen31b9b842021-08-31 12:51:40 -0700879 flags = append(flags, "-d=panic")
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700880 // 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.
Colin Cross7bb052a2015-02-03 12:59:37 -0800883 longdir := filepath.Join(cwd, t.goDirName())
Dan Willemsen0c157092016-07-08 13:57:52 -0700884 pkgs, err := goDirPackages(longdir, singlefilepkgs)
Colin Cross7bb052a2015-02-03 12:59:37 -0800885 if err != nil {
886 t.err = err
887 return
888 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700889 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 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700895 for i, pkg := range pkgs {
896 out, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700897 if i == errPkg {
Colin Cross7bb052a2015-02-03 12:59:37 -0800898 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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700910 for _, name := range pkg.files {
Colin Cross7bb052a2015-02-03 12:59:37 -0800911 fullshort = append(fullshort, filepath.Join(longdir, name), name)
912 }
Dan Willemsenbbdf6642017-01-13 22:57:23 -0800913 t.err = t.errorCheck(string(out), wantAuto, fullshort...)
Colin Cross7bb052a2015-02-03 12:59:37 -0800914 if t.err != nil {
915 break
916 }
917 }
Dan Willemsen0c157092016-07-08 13:57:52 -0700918 if action == "errorcheckdir" {
919 return
920 }
921 fallthrough
Colin Cross7bb052a2015-02-03 12:59:37 -0800922
923 case "rundir":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700924 // 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.
Colin Cross7bb052a2015-02-03 12:59:37 -0800928 longdir := filepath.Join(cwd, t.goDirName())
Dan Willemsen0c157092016-07-08 13:57:52 -0700929 pkgs, err := goDirPackages(longdir, singlefilepkgs)
Colin Cross7bb052a2015-02-03 12:59:37 -0800930 if err != nil {
931 t.err = err
932 return
933 }
Colin Crossefed6342019-09-07 08:34:44 -0700934 // 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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700944 for i, pkg := range pkgs {
945 _, err := compileInDir(runcmd, longdir, flags, pkg.name, pkg.files...)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -0700946 // 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) {
Colin Cross7bb052a2015-02-03 12:59:37 -0800949 t.err = err
950 return
951 }
952 if i == len(pkgs)-1 {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -0700953 err = linkFile(runcmd, pkg.files[0], ldflags)
Colin Cross7bb052a2015-02-03 12:59:37 -0800954 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 }
Dan Willemsen31b9b842021-08-31 12:51:40 -0700967 t.checkExpectedOutput(out)
Colin Cross7bb052a2015-02-03 12:59:37 -0800968 }
969 }
970
Colin Crossefed6342019-09-07 08:34:44 -0700971 case "runindir":
Patrice Arruda7f4776e2020-06-25 11:55:41 -0700972 // 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
Dan Willemsen59ee7802021-12-15 01:08:25 -0800997 cmd := []string{goTool(), "run", t.goGcflags()}
Colin Crossefed6342019-09-07 08:34:44 -0700998 if *linkshared {
999 cmd = append(cmd, "-linkshared")
1000 }
Dan Willemsen31b9b842021-08-31 12:51:40 -07001001 cmd = append(cmd, flags...)
Colin Crossefed6342019-09-07 08:34:44 -07001002 cmd = append(cmd, ".")
1003 out, err := runcmd(cmd...)
1004 if err != nil {
1005 t.err = err
1006 return
1007 }
Dan Willemsen31b9b842021-08-31 12:51:40 -07001008 t.checkExpectedOutput(out)
Colin Crossefed6342019-09-07 08:34:44 -07001009
Colin Cross7bb052a2015-02-03 12:59:37 -08001010 case "build":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001011 // Build Go file.
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001012 cmd := []string{goTool(), "build", t.goGcflags()}
1013 cmd = append(cmd, flags...)
1014 cmd = append(cmd, "-o", "a.exe", long)
1015 _, err := runcmd(cmd...)
Colin Cross7bb052a2015-02-03 12:59:37 -08001016 if err != nil {
1017 t.err = err
1018 }
1019
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001020 case "builddir", "buildrundir":
Dan Willemsenc78f7142017-07-26 13:08:14 -07001021 // Build an executable from all the .go and .s files in a subdirectory.
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001022 // Run it and verify its output in the buildrundir case.
Dan Willemsenc78f7142017-07-26 13:08:14 -07001023 longdir := filepath.Join(cwd, t.goDirName())
1024 files, dirErr := ioutil.ReadDir(longdir)
1025 if dirErr != nil {
1026 t.err = dirErr
1027 break
1028 }
Colin Cross1371fe42019-03-19 21:08:48 -07001029 var gos []string
1030 var asms []string
Dan Willemsenc78f7142017-07-26 13:08:14 -07001031 for _, file := range files {
1032 switch filepath.Ext(file.Name()) {
1033 case ".go":
Colin Cross1371fe42019-03-19 21:08:48 -07001034 gos = append(gos, filepath.Join(longdir, file.Name()))
Dan Willemsenc78f7142017-07-26 13:08:14 -07001035 case ".s":
Colin Cross1371fe42019-03-19 21:08:48 -07001036 asms = append(asms, filepath.Join(longdir, file.Name()))
Dan Willemsenc78f7142017-07-26 13:08:14 -07001037 }
1038
1039 }
Colin Cross1371fe42019-03-19 21:08:48 -07001040 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 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001046 cmd := []string{goTool(), "tool", "asm", "-p=main", "-gensymabis", "-o", "symabis"}
Colin Cross1371fe42019-03-19 21:08:48 -07001047 cmd = append(cmd, asms...)
1048 _, err = runcmd(cmd...)
1049 if err != nil {
1050 t.err = err
1051 break
1052 }
1053 }
Dan Willemsenc78f7142017-07-26 13:08:14 -07001054 var objs []string
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001055 cmd := []string{goTool(), "tool", "compile", "-p=main", "-e", "-D", ".", "-I", ".", "-o", "go.o"}
Dan Willemsene1b3b182018-02-27 19:36:27 -08001056 if len(asms) > 0 {
Colin Cross1371fe42019-03-19 21:08:48 -07001057 cmd = append(cmd, "-asmhdr", "go_asm.h", "-symabis", "symabis")
Dan Willemsene1b3b182018-02-27 19:36:27 -08001058 }
Colin Cross1371fe42019-03-19 21:08:48 -07001059 cmd = append(cmd, gos...)
Dan Willemsenc78f7142017-07-26 13:08:14 -07001060 _, 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 {
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001067 cmd = []string{goTool(), "tool", "asm", "-p=main", "-e", "-I", ".", "-o", "asm.o"}
Colin Cross1371fe42019-03-19 21:08:48 -07001068 cmd = append(cmd, asms...)
Dan Willemsenc78f7142017-07-26 13:08:14 -07001069 _, err = runcmd(cmd...)
1070 if err != nil {
1071 t.err = err
1072 break
1073 }
1074 objs = append(objs, "asm.o")
1075 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001076 cmd = []string{goTool(), "tool", "pack", "c", "all.a"}
Dan Willemsenc78f7142017-07-26 13:08:14 -07001077 cmd = append(cmd, objs...)
1078 _, err = runcmd(cmd...)
1079 if err != nil {
1080 t.err = err
1081 break
1082 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001083 cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"}
Dan Willemsenc78f7142017-07-26 13:08:14 -07001084 _, err = runcmd(cmd...)
1085 if err != nil {
1086 t.err = err
1087 break
1088 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001089 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 }
Dan Willemsen31b9b842021-08-31 12:51:40 -07001096 t.checkExpectedOutput(out)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001097 }
Dan Willemsenc78f7142017-07-26 13:08:14 -07001098
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001099 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.
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001102 // TODO: not supported on NaCl
Dan Willemsen59ee7802021-12-15 01:08:25 -08001103 cmd := []string{goTool(), "build", t.goGcflags(), "-o", "a.exe"}
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001104 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)
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001110 _, err := runcmd(cmd...)
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001111 if err != nil {
1112 t.err = err
1113 return
1114 }
1115 cmd = []string{"./a.exe"}
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001116 out, err := runcmd(append(cmd, args...)...)
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001117 if err != nil {
1118 t.err = err
1119 return
1120 }
1121
Dan Willemsen31b9b842021-08-31 12:51:40 -07001122 t.checkExpectedOutput(out)
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001123
Colin Cross7bb052a2015-02-03 12:59:37 -08001124 case "run":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001125 // Run Go file if no special go command flags are provided;
1126 // otherwise build an executable and run it.
1127 // Verify the output.
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001128 runInDir = ""
Dan Willemsene1b3b182018-02-27 19:36:27 -08001129 var out []byte
1130 var err error
Dan Willemsen59ee7802021-12-15 01:08:25 -08001131 if len(flags)+len(args) == 0 && t.goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS && goexp == env.GOEXPERIMENT {
Dan Willemsene1b3b182018-02-27 19:36:27 -08001132 // 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")
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001140 if _, err := runcmd(goTool(), "tool", "compile", "-p=main", "-o", pkg, t.goFileName()); err != nil {
Dan Willemsene1b3b182018-02-27 19:36:27 -08001141 t.err = err
1142 return
1143 }
1144 exe := filepath.Join(t.tempDir, "test.exe")
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001145 cmd := []string{goTool(), "tool", "link", "-s", "-w"}
Dan Willemsene1b3b182018-02-27 19:36:27 -08001146 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 {
Dan Willemsen59ee7802021-12-15 01:08:25 -08001153 cmd := []string{goTool(), "run", t.goGcflags()}
Dan Willemsene1b3b182018-02-27 19:36:27 -08001154 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...)...)
Dan Willemsen0c157092016-07-08 13:57:52 -07001160 }
Colin Cross7bb052a2015-02-03 12:59:37 -08001161 if err != nil {
1162 t.err = err
1163 return
1164 }
Dan Willemsen31b9b842021-08-31 12:51:40 -07001165 t.checkExpectedOutput(out)
Colin Cross7bb052a2015-02-03 12:59:37 -08001166
1167 case "runoutput":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001168 // Run Go file and write its output into temporary Go file.
1169 // Run generated Go file and verify its output.
Colin Cross7bb052a2015-02-03 12:59:37 -08001170 rungatec <- true
1171 defer func() {
1172 <-rungatec
1173 }()
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001174 runInDir = ""
Dan Willemsen59ee7802021-12-15 01:08:25 -08001175 cmd := []string{goTool(), "run", t.goGcflags()}
Dan Willemsen0c157092016-07-08 13:57:52 -07001176 if *linkshared {
1177 cmd = append(cmd, "-linkshared")
1178 }
1179 cmd = append(cmd, t.goFileName())
1180 out, err := runcmd(append(cmd, args...)...)
Colin Cross7bb052a2015-02-03 12:59:37 -08001181 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 }
Dan Willemsen59ee7802021-12-15 01:08:25 -08001190 cmd = []string{goTool(), "run", t.goGcflags()}
Dan Willemsen0c157092016-07-08 13:57:52 -07001191 if *linkshared {
1192 cmd = append(cmd, "-linkshared")
1193 }
1194 cmd = append(cmd, tfile)
1195 out, err = runcmd(cmd...)
Colin Cross7bb052a2015-02-03 12:59:37 -08001196 if err != nil {
1197 t.err = err
1198 return
1199 }
Dan Willemsen31b9b842021-08-31 12:51:40 -07001200 t.checkExpectedOutput(out)
Colin Cross7bb052a2015-02-03 12:59:37 -08001201
1202 case "errorcheckoutput":
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001203 // Run Go file and write its output into temporary Go file.
1204 // Compile and errorCheck generated Go file.
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001205 runInDir = ""
Dan Willemsen59ee7802021-12-15 01:08:25 -08001206 cmd := []string{goTool(), "run", t.goGcflags()}
Dan Willemsen0c157092016-07-08 13:57:52 -07001207 if *linkshared {
1208 cmd = append(cmd, "-linkshared")
1209 }
1210 cmd = append(cmd, t.goFileName())
1211 out, err := runcmd(append(cmd, args...)...)
Colin Cross7bb052a2015-02-03 12:59:37 -08001212 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 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001222 cmdline := []string{goTool(), "tool", "compile", "-p=p", "-d=panic", "-e", "-o", "a.o"}
Colin Cross7bb052a2015-02-03 12:59:37 -08001223 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 }
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001237 t.err = t.errorCheck(string(out), false, tfile, "tmp__.go")
Colin Cross7bb052a2015-02-03 12:59:37 -08001238 return
1239 }
1240}
1241
1242var execCmd []string
1243
1244func 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
1259func (t *test) String() string {
1260 return filepath.Join(t.dir, t.gofile)
1261}
1262
1263func (t *test) makeTempDir() {
1264 var err error
1265 t.tempDir, err = ioutil.TempDir("", "")
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001266 if err != nil {
1267 log.Fatal(err)
1268 }
Dan Willemsen0c157092016-07-08 13:57:52 -07001269 if *keep {
1270 log.Printf("Temporary directory is %s", t.tempDir)
1271 }
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001272 err = os.Mkdir(filepath.Join(t.tempDir, "test"), 0o755)
1273 if err != nil {
1274 log.Fatal(err)
1275 }
Colin Cross7bb052a2015-02-03 12:59:37 -08001276}
1277
Dan Willemsen31b9b842021-08-31 12:51:40 -07001278// 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.
1281func (t *test) checkExpectedOutput(gotBytes []byte) {
1282 got := string(gotBytes)
Colin Cross7bb052a2015-02-03 12:59:37 -08001283 filename := filepath.Join(t.dir, t.gofile)
1284 filename = filename[:len(filename)-len(".go")]
1285 filename += ".out"
Dan Willemsen31b9b842021-08-31 12:51:40 -07001286 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 }
Colin Cross7bb052a2015-02-03 12:59:37 -08001296}
1297
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001298func splitOutput(out string, wantAuto bool) []string {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001299 // 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
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001309 } else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001310 continue
1311 } else if strings.TrimSpace(line) != "" {
1312 res = append(res, line)
1313 }
1314 }
1315 return res
1316}
1317
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001318// 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>.
Colin Cross1371fe42019-03-19 21:08:48 -07001325// The <regexp> syntax is Perl but it's best to stick to egrep.
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001326//
1327// Sources files are supplied as fullshort slice.
Colin Cross1371fe42019-03-19 21:08:48 -07001328// It consists of pairs: full path to source file and its base name.
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001329func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) {
Colin Cross7bb052a2015-02-03 12:59:37 -08001330 defer func() {
1331 if *verbose && err != nil {
1332 log.Printf("%s gc output:\n%s", t, outStr)
1333 }
1334 }()
1335 var errs []error
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001336 out := splitOutput(outStr, wantAuto)
Colin Cross7bb052a2015-02-03 12:59:37 -08001337
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
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001354 if we.auto {
1355 errmsgs, out = partitionStrings("<autogenerated>", out)
1356 } else {
1357 errmsgs, out = partitionStrings(we.prefix, out)
1358 }
Colin Cross7bb052a2015-02-03 12:59:37 -08001359 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 {
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001366 // Assume errmsg says "file:line: foo".
1367 // Cut leading "file:line: " to avoid accidental matching of file name instead of message.
1368 text := errmsg
Dan Willemsen59ee7802021-12-15 01:08:25 -08001369 if _, suffix, ok := strings.Cut(text, " "); ok {
1370 text = suffix
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001371 }
1372 if we.re.MatchString(text) {
Colin Cross7bb052a2015-02-03 12:59:37 -08001373 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())
Dan Willemsen6ff23252015-09-15 13:49:18 -07001403}
Colin Cross7bb052a2015-02-03 12:59:37 -08001404
Dan Willemsen0c157092016-07-08 13:57:52 -07001405func (t *test) updateErrors(out, file string) {
1406 base := path.Base(file)
Dan Willemsen6ff23252015-09-15 13:49:18 -07001407 // 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.
Dan Willemsen59ee7802021-12-15 01:08:25 -08001415 for i := range lines {
1416 lines[i], _, _ = strings.Cut(lines[i], " // ERROR ")
Dan Willemsen6ff23252015-09-15 13:49:18 -07001417 }
1418 // Parse new errors.
1419 errors := make(map[int]map[string]bool)
1420 tmpRe := regexp.MustCompile(`autotmp_[0-9]+`)
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001421 for _, errStr := range splitOutput(out, false) {
Dan Willemsen59ee7802021-12-15 01:08:25 -08001422 errFile, rest, ok := strings.Cut(errStr, ":")
1423 if !ok || errFile != file {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001424 continue
1425 }
Dan Willemsen59ee7802021-12-15 01:08:25 -08001426 lineStr, msg, ok := strings.Cut(rest, ":")
1427 if !ok {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001428 continue
1429 }
Dan Willemsen59ee7802021-12-15 01:08:25 -08001430 line, err := strconv.Atoi(lineStr)
Dan Willemsen6ff23252015-09-15 13:49:18 -07001431 line--
1432 if err != nil || line < 0 || line >= len(lines) {
1433 continue
1434 }
Dan Willemsen0c157092016-07-08 13:57:52 -07001435 msg = strings.Replace(msg, file, base, -1) // normalize file mentions in error itself
1436 msg = strings.TrimLeft(msg, " \t")
Colin Cross1371fe42019-03-19 21:08:48 -07001437 for _, r := range []string{`\`, `*`, `+`, `?`, `[`, `]`, `(`, `)`} {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001438 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.
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001466 exec.Command(goTool(), "fmt", file).CombinedOutput()
Colin Cross7bb052a2015-02-03 12:59:37 -08001467}
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.
1472func 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
1489func 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
1500type wantedError struct {
1501 reStr string
1502 re *regexp.Regexp
1503 lineNum int
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001504 auto bool // match <autogenerated> line
Colin Cross7bb052a2015-02-03 12:59:37 -08001505 file string
1506 prefix string
1507}
1508
1509var (
1510 errRx = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001511 errAutoRx = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`)
Colin Cross7bb052a2015-02-03 12:59:37 -08001512 errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
1513 lineRx = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
1514)
1515
1516func (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 }
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001526 var auto bool
1527 m := errAutoRx.FindStringSubmatch(line)
1528 if m != nil {
1529 auto = true
1530 } else {
1531 m = errRx.FindStringSubmatch(line)
1532 }
Colin Cross7bb052a2015-02-03 12:59:37 -08001533 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 {
Dan Willemsen6ff23252015-09-15 13:49:18 -07001558 log.Fatalf("%s:%d: invalid regexp \"%s\" in ERROR line: %v", t.goFileName(), lineNum, rx, err)
Colin Cross7bb052a2015-02-03 12:59:37 -08001559 }
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,
Dan Willemsenbbdf6642017-01-13 22:57:23 -08001567 auto: auto,
Colin Cross7bb052a2015-02-03 12:59:37 -08001568 lineNum: lineNum,
1569 file: short,
1570 })
1571 }
1572 }
1573
1574 return
1575}
1576
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001577const (
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
1584var (
1585 // Regexp to split a line in code and comment, trimming spaces
Colin Cross1371fe42019-03-19 21:08:48 -07001586 rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?://\s*(.+)\s*)?$`)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001587
Colin Cross1371fe42019-03-19 21:08:48 -07001588 // 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 + `)*)`)
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001592
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{
Colin Cross846c3162021-05-14 11:11:40 -07001600 "386": {"GO386", "sse2", "softfloat"},
Dan Willemsen59ee7802021-12-15 01:08:25 -08001601 "amd64": {"GOAMD64", "v1", "v2", "v3", "v4"},
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001602 "arm": {"GOARM", "5", "6", "7"},
1603 "arm64": {},
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001604 "loong64": {},
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001605 "mips": {"GOMIPS", "hardfloat", "softfloat"},
1606 "mips64": {"GOMIPS64", "hardfloat", "softfloat"},
Colin Crossefed6342019-09-07 08:34:44 -07001607 "ppc64": {"GOPPC64", "power8", "power9"},
1608 "ppc64le": {"GOPPC64", "power8", "power9"},
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001609 "s390x": {},
Colin Crossefed6342019-09-07 08:34:44 -07001610 "wasm": {},
Dan Willemsen59ee7802021-12-15 01:08:25 -08001611 "riscv64": {},
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001612 }
1613)
1614
1615// wantedAsmOpcode is a single asmcheck check
1616type 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/")
1626type 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"}
1630func (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.
1646type asmChecks map[buildEnv]map[string][]wantedAsmOpcode
1647
1648// Envs returns all the buildEnv in which at least one check is present
1649func (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
1660func (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], ""
Colin Crossefed6342019-09-07 08:34:44 -07001690 if arch == "wasm" {
1691 os = "js"
1692 }
Dan Willemsenf3f2eb62018-08-28 11:28:58 -07001693 }
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
1758func (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
Colin Cross7bb052a2015-02-03 12:59:37 -08001842// defaultRunOutputLimit returns the number of runoutput tests that
1843// can be executed in parallel.
1844func 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.
1855func 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
Colin Cross7bb052a2015-02-03 12:59:37 -08001886func getenv(key, def string) string {
1887 value := os.Getenv(key)
1888 if value != "" {
1889 return value
1890 }
1891 return def
1892}
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001893
1894// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
1895func 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
Colin Cross846c3162021-05-14 11:11:40 -07001906 return filepath.WalkDir(srcRoot, func(srcPath string, d fs.DirEntry, err error) error {
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001907 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
Colin Cross846c3162021-05-14 11:11:40 -07001917 var info fs.FileInfo
1918 if d.Type()&os.ModeSymlink != 0 {
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001919 info, err = os.Stat(srcPath)
Colin Cross846c3162021-05-14 11:11:40 -07001920 } else {
1921 info, err = d.Info()
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001922 }
Colin Cross846c3162021-05-14 11:11:40 -07001923 if err != nil {
1924 return err
1925 }
1926 perm := info.Mode() & os.ModePerm
Patrice Arruda7f4776e2020-06-25 11:55:41 -07001927
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}
Dan Willemsen31b9b842021-08-31 12:51:40 -07001958
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001959// 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).
Dan Willemsen59ee7802021-12-15 01:08:25 -08001963
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001964// List of files that the compiler cannot errorcheck with the new typechecker (types2).
Dan Willemsen59ee7802021-12-15 01:08:25 -08001965var types2Failures = setOf(
Dan Willemsen14b5f992022-03-10 14:27:21 -08001966 "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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001971 "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)
Dan Willemsen14b5f992022-03-10 14:27:21 -08001973 "fixedbugs/issue31053.go", // types2 reports "unknown field" instead of "cannot refer to unexported field"
Dan Willemsen59ee7802021-12-15 01:08:25 -08001974)
1975
1976var 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
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001982var 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
Dan Willemsen59ee7802021-12-15 01:08:25 -08001985)
1986
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07001987// 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.
1991var _ = setOf(
Dan Willemsen14b5f992022-03-10 14:27:21 -08001992 "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
Dan Willemsen59ee7802021-12-15 01:08:25 -08002011var 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"
Dan Willemsen59ee7802021-12-15 01:08:25 -08002019 "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
Dan Willemsen59ee7802021-12-15 01:08:25 -08002024)
2025
2026func 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//
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07002044// a b:"c d" 'e''f' "g\""
Dan Willemsen59ee7802021-12-15 01:08:25 -08002045//
2046// Would be parsed as:
2047//
Dan Willemsen1cbb82a2022-08-08 23:13:50 -07002048// []string{"a", "b:c d", "ef", `g"`}
Dan Willemsen59ee7802021-12-15 01:08:25 -08002049//
2050// [copied from src/go/build/build.go]
2051func 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
Dan Willemsen31b9b842021-08-31 12:51:40 -07002094}