Source file
src/go/types/stdlib_test.go
1
2
3
4
5
6
7
8 package types_test
9
10 import (
11 "errors"
12 "fmt"
13 "go/ast"
14 "go/build"
15 "go/importer"
16 "go/parser"
17 "go/scanner"
18 "go/token"
19 "internal/testenv"
20 "os"
21 "path/filepath"
22 "runtime"
23 "slices"
24 "strings"
25 "sync"
26 "testing"
27 "time"
28
29 . "go/types"
30 )
31
32
33
34
35
36
37
38
39
40 var stdLibImporter = importer.ForCompiler(token.NewFileSet(), "source", nil)
41
42 func TestStdlib(t *testing.T) {
43 if testing.Short() {
44 t.Skip("skipping in short mode")
45 }
46
47 testenv.MustHaveGoBuild(t)
48
49
50 dirFiles := make(map[string][]string)
51 root := filepath.Join(testenv.GOROOT(t), "src")
52 walkPkgDirs(root, func(dir string, filenames []string) {
53 dirFiles[dir] = filenames
54 }, t.Error)
55
56 c := &stdlibChecker{
57 dirFiles: dirFiles,
58 pkgs: make(map[string]*futurePackage),
59 }
60
61 start := time.Now()
62
63
64
65
66
67
68
69 cpulimit := make(chan struct{}, runtime.GOMAXPROCS(0))
70 var wg sync.WaitGroup
71
72 for dir := range dirFiles {
73 cpulimit <- struct{}{}
74 wg.Add(1)
75 go func() {
76 defer func() {
77 wg.Done()
78 <-cpulimit
79 }()
80
81 _, err := c.getDirPackage(dir)
82 if err != nil {
83 t.Errorf("error checking %s: %v", dir, err)
84 }
85 }()
86 }
87
88 wg.Wait()
89
90 if testing.Verbose() {
91 fmt.Println(len(dirFiles), "packages typechecked in", time.Since(start))
92 }
93 }
94
95
96
97 type stdlibChecker struct {
98 dirFiles map[string][]string
99
100 mu sync.Mutex
101 pkgs map[string]*futurePackage
102 }
103
104
105 type futurePackage struct {
106 done chan struct{}
107 pkg *Package
108 err error
109 }
110
111 func (c *stdlibChecker) Import(path string) (*Package, error) {
112 panic("unimplemented: use ImportFrom")
113 }
114
115 func (c *stdlibChecker) ImportFrom(path, dir string, _ ImportMode) (*Package, error) {
116 if path == "unsafe" {
117
118 return Unsafe, nil
119 }
120
121 p, err := build.Default.Import(path, dir, build.FindOnly)
122 if err != nil {
123 return nil, err
124 }
125
126 pkg, err := c.getDirPackage(p.Dir)
127 if pkg != nil {
128
129
130 return pkg, nil
131 }
132 return nil, err
133 }
134
135
136
137
138
139 func (c *stdlibChecker) getDirPackage(dir string) (*Package, error) {
140 c.mu.Lock()
141 fut, ok := c.pkgs[dir]
142 if !ok {
143
144 fut = &futurePackage{
145 done: make(chan struct{}),
146 }
147 c.pkgs[dir] = fut
148 files, ok := c.dirFiles[dir]
149 c.mu.Unlock()
150 if !ok {
151 fut.err = fmt.Errorf("no files for %s", dir)
152 } else {
153
154
155
156 fut.pkg, fut.err = typecheckFiles(dir, files, c)
157 }
158 close(fut.done)
159 } else {
160
161 c.mu.Unlock()
162 <-fut.done
163 }
164 return fut.pkg, fut.err
165 }
166
167
168
169
170
171
172 func firstComment(filename string) string {
173 f, err := os.Open(filename)
174 if err != nil {
175 return ""
176 }
177 defer f.Close()
178
179 var src [4 << 10]byte
180 n, _ := f.Read(src[:])
181
182 var first string
183 var s scanner.Scanner
184 s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil , scanner.ScanComments)
185 for {
186 _, tok, lit := s.Scan()
187 switch tok {
188 case token.COMMENT:
189
190 if lit[1] == '*' {
191 lit = lit[:len(lit)-2]
192 }
193 contents := strings.TrimSpace(lit[2:])
194 if strings.HasPrefix(contents, "go:build ") {
195 return "skip"
196 }
197 if first == "" {
198 first = contents
199 }
200
201
202 case token.PACKAGE, token.EOF:
203 return first
204 }
205 }
206 }
207
208 func testTestDir(t *testing.T, path string, ignore ...string) {
209 files, err := os.ReadDir(path)
210 if err != nil {
211
212
213
214 if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "test")); os.IsNotExist(err) {
215 if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil {
216 t.Skipf("skipping: GOROOT/test not present")
217 }
218 }
219 t.Fatal(err)
220 }
221
222 excluded := make(map[string]bool)
223 for _, filename := range ignore {
224 excluded[filename] = true
225 }
226
227 fset := token.NewFileSet()
228 for _, f := range files {
229
230 if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
231 continue
232 }
233
234
235 expectErrors := false
236 filename := filepath.Join(path, f.Name())
237 goVersion := ""
238 if comment := firstComment(filename); comment != "" {
239 if strings.Contains(comment, "-goexperiment") {
240 continue
241 }
242 fields := strings.Fields(comment)
243 switch fields[0] {
244 case "skip", "compiledir":
245 continue
246 case "errorcheck":
247 expectErrors = true
248 for _, arg := range fields[1:] {
249 if arg == "-0" || arg == "-+" || arg == "-std" {
250
251
252
253
254 expectErrors = false
255 break
256 }
257 const prefix = "-lang="
258 if strings.HasPrefix(arg, prefix) {
259 goVersion = arg[len(prefix):]
260 }
261 }
262 }
263 }
264
265
266 file, err := parser.ParseFile(fset, filename, nil, 0)
267 if err == nil {
268 conf := Config{
269 GoVersion: goVersion,
270 Importer: stdLibImporter,
271 }
272 _, err = conf.Check(filename, fset, []*ast.File{file}, nil)
273 }
274
275 if expectErrors {
276 if err == nil {
277 t.Errorf("expected errors but found none in %s", filename)
278 }
279 } else {
280 if err != nil {
281 t.Error(err)
282 }
283 }
284 }
285 }
286
287 func TestStdTest(t *testing.T) {
288 testenv.MustHaveGoBuild(t)
289
290 if testing.Short() && testenv.Builder() == "" {
291 t.Skip("skipping in short mode")
292 }
293
294 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"),
295 "cmplxdivide.go",
296 "directive.go",
297 "directive2.go",
298 "embedfunc.go",
299 "embedvers.go",
300 "linkname2.go",
301 "linkname3.go",
302 )
303 }
304
305 func TestStdFixed(t *testing.T) {
306 testenv.MustHaveGoBuild(t)
307
308 if testing.Short() && testenv.Builder() == "" {
309 t.Skip("skipping in short mode")
310 }
311
312 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"),
313 "bug248.go", "bug302.go", "bug369.go",
314 "bug398.go",
315 "issue6889.go",
316 "issue11362.go",
317 "issue16369.go",
318 "issue18459.go",
319 "issue18882.go",
320 "issue20027.go",
321 "issue20529.go",
322 "issue22200.go",
323 "issue22200b.go",
324 "issue25507.go",
325 "issue20780.go",
326 "bug251.go",
327 "issue42058a.go",
328 "issue42058b.go",
329 "issue48097.go",
330 "issue48230.go",
331 "issue49767.go",
332 "issue49814.go",
333 "issue78355.go",
334 "issue56103.go",
335 "issue52697.go",
336
337
338
339 "bug514.go",
340 "issue40954.go",
341 "issue42032.go",
342 "issue42076.go",
343 "issue46903.go",
344 "issue51733.go",
345 "notinheap2.go",
346 "notinheap3.go",
347 )
348 }
349
350 func TestStdKen(t *testing.T) {
351 testenv.MustHaveGoBuild(t)
352
353 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken"))
354 }
355
356
357 var excluded = map[string]bool{
358 "builtin": true,
359 "cmd/compile/internal/ssa/_gen": true,
360 "runtime/_mkmalloc": true,
361 "simd/archsimd/_gen/simdgen": true,
362 "simd/archsimd/_gen/unify": true,
363 }
364
365
366
367
368
369
370 var printPackageMu sync.Mutex
371
372
373 func typecheckFiles(path string, filenames []string, importer Importer) (*Package, error) {
374 fset := token.NewFileSet()
375
376
377 var files []*ast.File
378 for _, filename := range filenames {
379 file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
380 if err != nil {
381 return nil, err
382 }
383
384 files = append(files, file)
385 }
386
387 if testing.Verbose() {
388 printPackageMu.Lock()
389 fmt.Println("package", files[0].Name.Name)
390 for _, filename := range filenames {
391 fmt.Println("\t", filename)
392 }
393 printPackageMu.Unlock()
394 }
395
396
397 var errs []error
398 conf := Config{
399 Error: func(err error) {
400 errs = append(errs, err)
401 },
402 Importer: importer,
403 }
404 info := Info{Uses: make(map[*ast.Ident]Object)}
405 pkg, _ := conf.Check(path, fset, files, &info)
406 err := errors.Join(errs...)
407 if err != nil {
408 return pkg, err
409 }
410
411
412
413
414 errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0)
415 for id, obj := range info.Uses {
416 predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
417 if predeclared == (obj.Pkg() != nil) {
418 posn := fset.Position(id.Pos())
419 if predeclared {
420 return nil, fmt.Errorf("%s: predeclared object with package: %s", posn, obj)
421 } else {
422 return nil, fmt.Errorf("%s: user-defined object without package: %s", posn, obj)
423 }
424 }
425 }
426
427 return pkg, nil
428 }
429
430
431 func pkgFilenames(dir string, includeTest bool) ([]string, error) {
432 ctxt := build.Default
433 ctxt.CgoEnabled = false
434 pkg, err := ctxt.ImportDir(dir, 0)
435 if err != nil {
436 if _, nogo := err.(*build.NoGoError); nogo {
437 return nil, nil
438 }
439 return nil, err
440 }
441 if excluded[pkg.ImportPath] {
442 return nil, nil
443 }
444 if slices.Contains(strings.Split(pkg.ImportPath, "/"), "_asm") {
445
446
447 return nil, nil
448 }
449 var filenames []string
450 for _, name := range pkg.GoFiles {
451 filenames = append(filenames, filepath.Join(pkg.Dir, name))
452 }
453 if includeTest {
454 for _, name := range pkg.TestGoFiles {
455 filenames = append(filenames, filepath.Join(pkg.Dir, name))
456 }
457 }
458 return filenames, nil
459 }
460
461 func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...any)) {
462 w := walker{pkgh, errh}
463 w.walk(dir)
464 }
465
466 type walker struct {
467 pkgh func(dir string, filenames []string)
468 errh func(args ...any)
469 }
470
471 func (w *walker) walk(dir string) {
472 files, err := os.ReadDir(dir)
473 if err != nil {
474 w.errh(err)
475 return
476 }
477
478
479
480
481 pkgFiles, err := pkgFilenames(dir, false)
482 if err != nil {
483 w.errh(err)
484 return
485 }
486 if pkgFiles != nil {
487 w.pkgh(dir, pkgFiles)
488 }
489
490
491 for _, f := range files {
492 if f.IsDir() && f.Name() != "testdata" {
493 w.walk(filepath.Join(dir, f.Name()))
494 }
495 }
496 }
497
View as plain text