github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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 "io/ioutil" 20 "os" 21 "path/filepath" 22 "runtime" 23 "strings" 24 "testing" 25 "time" 26 27 . "go/types" 28 ) 29 30 var ( 31 pkgCount int // number of packages processed 32 start time.Time 33 34 // Use the same importer for all std lib tests to 35 // avoid repeated importing of the same packages. 36 stdLibImporter = importer.Default() 37 ) 38 39 func TestStdlib(t *testing.T) { 40 testenv.MustHaveGoBuild(t) 41 42 start = time.Now() 43 walkDirs(t, filepath.Join(runtime.GOROOT(), "src")) 44 if testing.Verbose() { 45 fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) 46 } 47 } 48 49 // firstComment returns the contents of the first comment in 50 // the given file, assuming there's one within the first KB. 51 func firstComment(filename string) string { 52 f, err := os.Open(filename) 53 if err != nil { 54 return "" 55 } 56 defer f.Close() 57 58 var src [1 << 10]byte // read at most 1KB 59 n, _ := f.Read(src[:]) 60 61 var s scanner.Scanner 62 s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments) 63 for { 64 _, tok, lit := s.Scan() 65 switch tok { 66 case token.COMMENT: 67 // remove trailing */ of multi-line comment 68 if lit[1] == '*' { 69 lit = lit[:len(lit)-2] 70 } 71 return strings.TrimSpace(lit[2:]) 72 case token.EOF: 73 return "" 74 } 75 } 76 } 77 78 func testTestDir(t *testing.T, path string, ignore ...string) { 79 files, err := ioutil.ReadDir(path) 80 if err != nil { 81 t.Fatal(err) 82 } 83 84 excluded := make(map[string]bool) 85 for _, filename := range ignore { 86 excluded[filename] = true 87 } 88 89 fset := token.NewFileSet() 90 for _, f := range files { 91 // filter directory contents 92 if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] { 93 continue 94 } 95 96 // get per-file instructions 97 expectErrors := false 98 filename := filepath.Join(path, f.Name()) 99 if comment := firstComment(filename); comment != "" { 100 fields := strings.Fields(comment) 101 switch fields[0] { 102 case "skip", "compiledir": 103 continue // ignore this file 104 case "errorcheck": 105 expectErrors = true 106 for _, arg := range fields[1:] { 107 if arg == "-0" || arg == "-+" || arg == "-std" { 108 // Marked explicitly as not expected errors (-0), 109 // or marked as compiling runtime/stdlib, which is only done 110 // to trigger runtime/stdlib-only error output. 111 // In both cases, the code should typecheck. 112 expectErrors = false 113 break 114 } 115 } 116 } 117 } 118 119 // parse and type-check file 120 file, err := parser.ParseFile(fset, filename, nil, 0) 121 if err == nil { 122 conf := Config{Importer: stdLibImporter} 123 _, err = conf.Check(filename, fset, []*ast.File{file}, nil) 124 } 125 126 if expectErrors { 127 if err == nil { 128 t.Errorf("expected errors but found none in %s", filename) 129 } 130 } else { 131 if err != nil { 132 t.Error(err) 133 } 134 } 135 } 136 } 137 138 func TestStdTest(t *testing.T) { 139 testenv.MustHaveGoBuild(t) 140 141 if testing.Short() && testenv.Builder() == "" { 142 t.Skip("skipping in short mode") 143 } 144 145 // test/recover4.go is only built for Linux and Darwin. 146 // TODO(gri) Remove once tests consider +build tags (issue 10370). 147 if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { 148 return 149 } 150 151 testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), 152 "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore 153 "sigchld.go", // don't work on Windows; testTestDir should consult build tags 154 ) 155 } 156 157 func TestStdFixed(t *testing.T) { 158 testenv.MustHaveGoBuild(t) 159 160 if testing.Short() && testenv.Builder() == "" { 161 t.Skip("skipping in short mode") 162 } 163 164 testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), 165 "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore 166 "issue6889.go", // gc-specific test 167 "issue7746.go", // large constants - consumes too much memory 168 "issue11362.go", // canonical import path check 169 "issue15002.go", // uses Mmap; testTestDir should consult build tags 170 "issue16369.go", // go/types handles this correctly - not an issue 171 "issue18459.go", // go/types doesn't check validity of //go:xxx directives 172 "issue18882.go", // go/types doesn't check validity of //go:xxx directives 173 "issue20232.go", // go/types handles larger constants than gc 174 "issue20529.go", // go/types does not have constraints on stack size 175 "issue22200.go", // go/types does not have constraints on stack size 176 "issue22200b.go", // go/types does not have constraints on stack size 177 ) 178 } 179 180 func TestStdKen(t *testing.T) { 181 testenv.MustHaveGoBuild(t) 182 183 testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) 184 } 185 186 // Package paths of excluded packages. 187 var excluded = map[string]bool{ 188 "builtin": true, 189 } 190 191 // typecheck typechecks the given package files. 192 func typecheck(t *testing.T, path string, filenames []string) { 193 fset := token.NewFileSet() 194 195 // parse package files 196 var files []*ast.File 197 for _, filename := range filenames { 198 file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) 199 if err != nil { 200 // the parser error may be a list of individual errors; report them all 201 if list, ok := err.(scanner.ErrorList); ok { 202 for _, err := range list { 203 t.Error(err) 204 } 205 return 206 } 207 t.Error(err) 208 return 209 } 210 211 if testing.Verbose() { 212 if len(files) == 0 { 213 fmt.Println("package", file.Name.Name) 214 } 215 fmt.Println("\t", filename) 216 } 217 218 files = append(files, file) 219 } 220 221 // typecheck package files 222 conf := Config{ 223 Error: func(err error) { t.Error(err) }, 224 Importer: stdLibImporter, 225 } 226 info := Info{Uses: make(map[*ast.Ident]Object)} 227 conf.Check(path, fset, files, &info) 228 pkgCount++ 229 230 // Perform checks of API invariants. 231 232 // All Objects have a package, except predeclared ones. 233 errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error 234 for id, obj := range info.Uses { 235 predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError 236 if predeclared == (obj.Pkg() != nil) { 237 posn := fset.Position(id.Pos()) 238 if predeclared { 239 t.Errorf("%s: predeclared object with package: %s", posn, obj) 240 } else { 241 t.Errorf("%s: user-defined object without package: %s", posn, obj) 242 } 243 } 244 } 245 } 246 247 // pkgFilenames returns the list of package filenames for the given directory. 248 func pkgFilenames(dir string) ([]string, error) { 249 ctxt := build.Default 250 ctxt.CgoEnabled = false 251 pkg, err := ctxt.ImportDir(dir, 0) 252 if err != nil { 253 if _, nogo := err.(*build.NoGoError); nogo { 254 return nil, nil // no *.go files, not an error 255 } 256 return nil, err 257 } 258 if excluded[pkg.ImportPath] { 259 return nil, nil 260 } 261 var filenames []string 262 for _, name := range pkg.GoFiles { 263 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 264 } 265 for _, name := range pkg.TestGoFiles { 266 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 267 } 268 return filenames, nil 269 } 270 271 // Note: Could use filepath.Walk instead of walkDirs but that wouldn't 272 // necessarily be shorter or clearer after adding the code to 273 // terminate early for -short tests. 274 275 func walkDirs(t *testing.T, dir string) { 276 // limit run time for short tests 277 if testing.Short() && time.Since(start) >= 10*time.Millisecond { 278 return 279 } 280 281 fis, err := ioutil.ReadDir(dir) 282 if err != nil { 283 t.Error(err) 284 return 285 } 286 287 // typecheck package in directory 288 // but ignore files directly under $GOROOT/src (might be temporary test files). 289 if dir != filepath.Join(runtime.GOROOT(), "src") { 290 files, err := pkgFilenames(dir) 291 if err != nil { 292 t.Error(err) 293 return 294 } 295 if files != nil { 296 typecheck(t, dir, files) 297 } 298 } 299 300 // traverse subdirectories, but don't walk into testdata 301 for _, fi := range fis { 302 if fi.IsDir() && fi.Name() != "testdata" { 303 walkDirs(t, filepath.Join(dir, fi.Name())) 304 } 305 } 306 }