github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/go/types/stdlib_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file tests types.Check by using it to 6 // typecheck the standard library and tests. 7 8 package types_test 9 10 import ( 11 "fmt" 12 "go/ast" 13 "go/build" 14 "go/importer" 15 "go/parser" 16 "go/scanner" 17 "go/token" 18 "internal/testenv" 19 "os" 20 "path/filepath" 21 "strings" 22 "testing" 23 "time" 24 25 . "go/types" 26 ) 27 28 // The cmd/*/internal packages may have been deleted as part of a binary 29 // release. Import from source instead. 30 // 31 // (See https://golang.org/issue/43232 and 32 // https://github.com/golang/build/blob/df58bbac082bc87c4a3cdfe336d1ffe60bbaa916/cmd/release/release.go#L533-L545.) 33 // 34 // Use the same importer for all std lib tests to 35 // avoid repeated importing of the same packages. 36 var stdLibImporter = importer.ForCompiler(token.NewFileSet(), "source", nil) 37 38 func TestStdlib(t *testing.T) { 39 testenv.MustHaveGoBuild(t) 40 41 pkgCount := 0 42 duration := walkPkgDirs(filepath.Join(testenv.GOROOT(t), "src"), func(dir string, filenames []string) { 43 typecheckFiles(t, dir, filenames) 44 pkgCount++ 45 }, t.Error) 46 47 if testing.Verbose() { 48 fmt.Println(pkgCount, "packages typechecked in", duration) 49 } 50 } 51 52 // firstComment returns the contents of the first non-empty comment in 53 // the given file, "skip", or the empty string. No matter the present 54 // comments, if any of them contains a build tag, the result is always 55 // "skip". Only comments before the "package" token and within the first 56 // 4K of the file are considered. 57 func firstComment(filename string) string { 58 f, err := os.Open(filename) 59 if err != nil { 60 return "" 61 } 62 defer f.Close() 63 64 var src [4 << 10]byte // read at most 4KB 65 n, _ := f.Read(src[:]) 66 67 var first string 68 var s scanner.Scanner 69 s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil /* ignore errors */, scanner.ScanComments) 70 for { 71 _, tok, lit := s.Scan() 72 switch tok { 73 case token.COMMENT: 74 // remove trailing */ of multi-line comment 75 if lit[1] == '*' { 76 lit = lit[:len(lit)-2] 77 } 78 contents := strings.TrimSpace(lit[2:]) 79 if strings.HasPrefix(contents, "+build ") { 80 return "skip" 81 } 82 if first == "" { 83 first = contents // contents may be "" but that's ok 84 } 85 // continue as we may still see build tags 86 87 case token.PACKAGE, token.EOF: 88 return first 89 } 90 } 91 } 92 93 func testTestDir(t *testing.T, path string, ignore ...string) { 94 files, err := os.ReadDir(path) 95 if err != nil { 96 t.Fatal(err) 97 } 98 99 excluded := make(map[string]bool) 100 for _, filename := range ignore { 101 excluded[filename] = true 102 } 103 104 fset := token.NewFileSet() 105 for _, f := range files { 106 // filter directory contents 107 if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] { 108 continue 109 } 110 111 // get per-file instructions 112 expectErrors := false 113 filename := filepath.Join(path, f.Name()) 114 goVersion := "" 115 if comment := firstComment(filename); comment != "" { 116 fields := strings.Fields(comment) 117 switch fields[0] { 118 case "skip", "compiledir": 119 continue // ignore this file 120 case "errorcheck": 121 expectErrors = true 122 for _, arg := range fields[1:] { 123 if arg == "-0" || arg == "-+" || arg == "-std" { 124 // Marked explicitly as not expecting errors (-0), 125 // or marked as compiling runtime/stdlib, which is only done 126 // to trigger runtime/stdlib-only error output. 127 // In both cases, the code should typecheck. 128 expectErrors = false 129 break 130 } 131 const prefix = "-lang=" 132 if strings.HasPrefix(arg, prefix) { 133 goVersion = arg[len(prefix):] 134 } 135 } 136 } 137 } 138 139 // parse and type-check file 140 file, err := parser.ParseFile(fset, filename, nil, 0) 141 if err == nil { 142 conf := Config{ 143 GoVersion: goVersion, 144 Importer: stdLibImporter, 145 } 146 _, err = conf.Check(filename, fset, []*ast.File{file}, nil) 147 } 148 149 if expectErrors { 150 if err == nil { 151 t.Errorf("expected errors but found none in %s", filename) 152 } 153 } else { 154 if err != nil { 155 t.Error(err) 156 } 157 } 158 } 159 } 160 161 func TestStdTest(t *testing.T) { 162 testenv.MustHaveGoBuild(t) 163 164 if testing.Short() && testenv.Builder() == "" { 165 t.Skip("skipping in short mode") 166 } 167 168 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"), 169 "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore 170 "directive.go", // tests compiler rejection of bad directive placement - ignore 171 "directive2.go", // tests compiler rejection of bad directive placement - ignore 172 "embedfunc.go", // tests //go:embed 173 "embedvers.go", // tests //go:embed 174 "linkname2.go", // go/types doesn't check validity of //go:xxx directives 175 "linkname3.go", // go/types doesn't check validity of //go:xxx directives 176 ) 177 } 178 179 func TestStdFixed(t *testing.T) { 180 testenv.MustHaveGoBuild(t) 181 182 if testing.Short() && testenv.Builder() == "" { 183 t.Skip("skipping in short mode") 184 } 185 186 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"), 187 "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore 188 "issue6889.go", // gc-specific test 189 "issue11362.go", // canonical import path check 190 "issue16369.go", // go/types handles this correctly - not an issue 191 "issue18459.go", // go/types doesn't check validity of //go:xxx directives 192 "issue18882.go", // go/types doesn't check validity of //go:xxx directives 193 "issue20529.go", // go/types does not have constraints on stack size 194 "issue22200.go", // go/types does not have constraints on stack size 195 "issue22200b.go", // go/types does not have constraints on stack size 196 "issue25507.go", // go/types does not have constraints on stack size 197 "issue20780.go", // go/types does not have constraints on stack size 198 "bug251.go", // go.dev/issue/34333 which was exposed with fix for go.dev/issue/34151 199 "issue42058a.go", // go/types does not have constraints on channel element size 200 "issue42058b.go", // go/types does not have constraints on channel element size 201 "issue48097.go", // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function 202 "issue48230.go", // go/types doesn't check validity of //go:xxx directives 203 "issue49767.go", // go/types does not have constraints on channel element size 204 "issue49814.go", // go/types does not have constraints on array size 205 "issue56103.go", // anonymous interface cycles; will be a type checker error in 1.22 206 207 // These tests requires runtime/cgo.Incomplete, which is only available on some platforms. 208 // However, go/types does not know about build constraints. 209 "bug514.go", 210 "issue40954.go", 211 "issue42032.go", 212 "issue42076.go", 213 "issue46903.go", 214 "issue51733.go", 215 "notinheap2.go", 216 "notinheap3.go", 217 ) 218 } 219 220 func TestStdKen(t *testing.T) { 221 testenv.MustHaveGoBuild(t) 222 223 testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken")) 224 } 225 226 // Package paths of excluded packages. 227 var excluded = map[string]bool{ 228 "builtin": true, 229 230 // See go.dev/issue/46027: some imports are missing for this submodule. 231 "crypto/internal/edwards25519/field/_asm": true, 232 "crypto/internal/bigmod/_asm": true, 233 } 234 235 // typecheckFiles typechecks the given package files. 236 func typecheckFiles(t *testing.T, path string, filenames []string) { 237 fset := token.NewFileSet() 238 239 // parse package files 240 var files []*ast.File 241 for _, filename := range filenames { 242 file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) 243 if err != nil { 244 // the parser error may be a list of individual errors; report them all 245 if list, ok := err.(scanner.ErrorList); ok { 246 for _, err := range list { 247 t.Error(err) 248 } 249 return 250 } 251 t.Error(err) 252 return 253 } 254 255 if testing.Verbose() { 256 if len(files) == 0 { 257 fmt.Println("package", file.Name.Name) 258 } 259 fmt.Println("\t", filename) 260 } 261 262 files = append(files, file) 263 } 264 265 // typecheck package files 266 conf := Config{ 267 Error: func(err error) { 268 t.Helper() 269 t.Error(err) 270 }, 271 Importer: stdLibImporter, 272 } 273 info := Info{Uses: make(map[*ast.Ident]Object)} 274 conf.Check(path, fset, files, &info) 275 276 // Perform checks of API invariants. 277 278 // All Objects have a package, except predeclared ones. 279 errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error 280 for id, obj := range info.Uses { 281 predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError 282 if predeclared == (obj.Pkg() != nil) { 283 posn := fset.Position(id.Pos()) 284 if predeclared { 285 t.Errorf("%s: predeclared object with package: %s", posn, obj) 286 } else { 287 t.Errorf("%s: user-defined object without package: %s", posn, obj) 288 } 289 } 290 } 291 } 292 293 // pkgFilenames returns the list of package filenames for the given directory. 294 func pkgFilenames(dir string) ([]string, error) { 295 ctxt := build.Default 296 ctxt.CgoEnabled = false 297 pkg, err := ctxt.ImportDir(dir, 0) 298 if err != nil { 299 if _, nogo := err.(*build.NoGoError); nogo { 300 return nil, nil // no *.go files, not an error 301 } 302 return nil, err 303 } 304 if excluded[pkg.ImportPath] { 305 return nil, nil 306 } 307 var filenames []string 308 for _, name := range pkg.GoFiles { 309 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 310 } 311 for _, name := range pkg.TestGoFiles { 312 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 313 } 314 return filenames, nil 315 } 316 317 func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...any)) time.Duration { 318 w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh} 319 w.walk(dir) 320 return time.Since(w.start) 321 } 322 323 type walker struct { 324 start time.Time 325 dmax time.Duration 326 pkgh func(dir string, filenames []string) 327 errh func(args ...any) 328 } 329 330 func (w *walker) walk(dir string) { 331 // limit run time for short tests 332 if testing.Short() && time.Since(w.start) >= w.dmax { 333 return 334 } 335 336 files, err := os.ReadDir(dir) 337 if err != nil { 338 w.errh(err) 339 return 340 } 341 342 // apply pkgh to the files in directory dir 343 pkgFiles, err := pkgFilenames(dir) 344 if err != nil { 345 w.errh(err) 346 return 347 } 348 if pkgFiles != nil { 349 w.pkgh(dir, pkgFiles) 350 } 351 352 // traverse subdirectories, but don't walk into testdata 353 for _, f := range files { 354 if f.IsDir() && f.Name() != "testdata" { 355 w.walk(filepath.Join(dir, f.Name())) 356 } 357 } 358 }