gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/nogo/check/check.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package check implements binary analysis similar to bazel's nogo, or the 16 // unitchecker package. It exists in order to provide additional facilities for 17 // analysis, namely plumbing through the output from dumping the generated 18 // binary (to analyze actual produced code). 19 package check 20 21 import ( 22 "errors" 23 "fmt" 24 "go/ast" 25 "go/build" 26 "go/parser" 27 "go/token" 28 "go/types" 29 "io" 30 "os" 31 "path" 32 "path/filepath" 33 "reflect" 34 "regexp" 35 "runtime/debug" 36 "strings" 37 "sync" 38 39 "golang.org/x/tools/go/analysis" 40 "golang.org/x/tools/go/gcexportdata" 41 "gvisor.dev/gvisor/runsc/flag" 42 "gvisor.dev/gvisor/tools/nogo/facts" 43 "gvisor.dev/gvisor/tools/nogo/flags" 44 ) 45 46 var ( 47 // ErrSkip indicates the package should be skipped. 48 ErrSkip = errors.New("skipped") 49 50 // showTimes indicates we should show analyzer times. 51 showTimes = flag.Bool("show_times", false, "show all analyzer times") 52 ) 53 54 var ( 55 tagsOnce sync.Once 56 buildTags []string 57 releaseTagsVal []string 58 releaseTagsErr error 59 ) 60 61 // Hack! factFacts only provides facts loaded from directly imported packages 62 // for efficiency (see importer.cache). In general, if you need a fact from a 63 // package that isn't otherwise imported, the expectation is that you will add 64 // a dummy import/use of the desired package to ensure it is a dependency. 65 // 66 // Unfortunately, some packages need facts from internal packages. Since 67 // internal packages cannot be imported we explicitly import in this tool to 68 // ensure the facts are available to ImportPackageFact. 69 var internalPackages = []string{ 70 // Required by pkg/sync for internal/abi.MapType. 71 "internal/abi", 72 } 73 74 // shouldInclude indicates whether the file should be included. 75 func shouldInclude(path string) (bool, error) { 76 tagsOnce.Do(func() { 77 if len(flags.BuildTags) > 0 { 78 buildTags = strings.Split(flags.BuildTags, ",") 79 } 80 releaseTagsVal, releaseTagsErr = releaseTags() 81 }) 82 if releaseTagsErr != nil { 83 return false, releaseTagsErr 84 } 85 ctx := build.Default 86 ctx.GOOS = flags.GOOS 87 ctx.GOARCH = flags.GOARCH 88 ctx.BuildTags = buildTags 89 ctx.ReleaseTags = releaseTagsVal 90 return ctx.MatchFile(filepath.Dir(path), filepath.Base(path)) 91 } 92 93 // sortSrcs sorts a set of src files into Go files and non-Go files. 94 func sortSrcs(srcs []string) (goFiles []string, nonGoFiles []string) { 95 for _, filename := range srcs { 96 if strings.HasSuffix(filename, ".go") { 97 goFiles = append(goFiles, filename) 98 } else { 99 nonGoFiles = append(nonGoFiles, filename) 100 } 101 } 102 return 103 } 104 105 // importerEntry is a single entry in the importer. 106 type importerEntry struct { 107 ready sync.WaitGroup 108 pkg *types.Package 109 findings FindingSet 110 err error 111 factsMu sync.Mutex 112 facts *facts.Package 113 } 114 115 // importer is an almost-implementation of go/types.Importer. 116 // 117 // This wraps a configuration, which provides the map of package names to 118 // files, and the facts. Note that this importer implementation will always 119 // pass when a given package is not available. 120 type importer struct { 121 fset *token.FileSet 122 sources map[string][]string 123 124 // mu protects cache & bundles (see below). 125 mu sync.Mutex 126 cache map[string]*importerEntry 127 128 // bundles is protected by mu, but once set is immutable. 129 bundles []*facts.Bundle 130 131 // importsMu protects imports. 132 importsMu sync.Mutex 133 imports map[string]*types.Package 134 } 135 136 // loadBundles loads all bundle files. 137 // 138 // This should only be called from loadFacts, below. After calling this 139 // function, i.bundles may be read freely without holding a lock. 140 func (i *importer) loadBundles() error { 141 i.mu.Lock() 142 defer i.mu.Unlock() 143 144 // Are bundles already available? 145 if i.bundles != nil { 146 return nil 147 } 148 149 // Scan all bundle files. 150 for _, filename := range flags.Bundles { 151 // Open the given filename as a bundle. 152 loadedFacts, err := facts.BundleFrom(filename) 153 if err != nil { 154 return fmt.Errorf("error loading bundled facts: %w", err) 155 } 156 157 // Add to the set of available bundles. 158 i.bundles = append(i.bundles, loadedFacts) 159 } 160 161 return nil 162 } 163 164 // loadFacts returns all package facts for the given name. 165 // 166 // This should be called only from importPackage, as this may deserialize a 167 // facts file (which is an expensive operation). Callers should generally rely 168 // on fastFacts to access facts for packages that have already been imported. 169 func (i *importer) loadFacts(pkg *types.Package) (*facts.Package, error) { 170 // Attempt to load from the fact map. 171 filename, ok := flags.FactMap[pkg.Path()] 172 if ok { 173 r, openErr := os.Open(filename) 174 if openErr != nil { 175 return nil, fmt.Errorf("error loading facts from %q: %w", filename, openErr) 176 } 177 defer r.Close() 178 loadedFacts := facts.NewPackage() 179 if readErr := loadedFacts.ReadFrom(pkg, r); readErr != nil { 180 return nil, fmt.Errorf("error loading facts: %w", readErr) 181 } 182 return loadedFacts, nil 183 } 184 185 // Attempt to load any bundles. 186 if err := i.loadBundles(); err != nil { 187 return nil, fmt.Errorf("error loading bundles: %w", err) 188 } 189 190 // Try to import from the bundle. 191 for _, bundleFacts := range i.bundles { 192 localFacts, err := bundleFacts.Package(pkg) 193 if err != nil { 194 return nil, fmt.Errorf("error loading from a bundle: %w", err) 195 } 196 if localFacts != nil { 197 return localFacts, nil 198 } 199 } 200 201 // Nothing available for this package? 202 return nil, nil 203 } 204 205 // fastFacts returns facts for the given package. 206 // 207 // This relies exclusively on loaded packages, as the parameter is 208 // *types.Package and therefore the package data must already be available. 209 func (i *importer) fastFacts(pkg *types.Package) *facts.Package { 210 i.mu.Lock() 211 e, ok := i.cache[pkg.Path()] 212 i.mu.Unlock() 213 if !ok { 214 return nil 215 } 216 217 e.factsMu.Lock() 218 defer e.factsMu.Unlock() 219 220 // Do we have them already? 221 if e.facts != nil { 222 return e.facts 223 } 224 225 // Load the facts. 226 facts, err := i.loadFacts(pkg) 227 if err != nil { 228 // There are no facts available, but no good way to propagate 229 // this minor error. It may be intentional that no analysis was 230 // performed on some part of the standard library, for example. 231 return nil 232 } 233 e.facts = facts // Cache the result. 234 return facts 235 } 236 237 // findArchive finds the archive for the given package. 238 func (i *importer) findArchive(path string) (rc io.ReadCloser, err error) { 239 realPath, ok := flags.ArchiveMap[path] 240 if !ok { 241 return i.findBinary(path) 242 } 243 return os.Open(realPath) 244 } 245 246 // findBinary finds the binary for the given package. 247 func (i *importer) findBinary(path string) (rc io.ReadCloser, err error) { 248 realPath, ok := flags.ImportMap[path] 249 if !ok { 250 // Not found in the import path. Attempt to find the package 251 // via the standard library. 252 rc, err = findStdPkg(path) 253 } else { 254 // Open the file. 255 rc, err = os.Open(realPath) 256 } 257 return rc, err 258 } 259 260 // importPackage almost-implements types.Importer.Import. 261 // 262 // This must be called by other methods directly. 263 func (i *importer) importPackage(path string) (*types.Package, error) { 264 if path == "unsafe" { 265 // Special case: go/types has pre-defined type information for 266 // unsafe. We ensure that this package is correct, in case any 267 // analyzers are specifically looking for this. 268 return types.Unsafe, nil 269 } 270 271 // Pull the internal entry. 272 i.mu.Lock() 273 entry, ok := i.cache[path] 274 if ok && entry.pkg != nil { 275 i.mu.Unlock() 276 entry.ready.Wait() 277 return entry.pkg, entry.err 278 } 279 280 // Start preparing this entry. 281 entry = new(importerEntry) 282 entry.ready.Add(1) 283 defer entry.ready.Done() 284 i.cache[path] = entry 285 i.mu.Unlock() 286 287 // If we have the srcs for this package, then we can actually do an 288 // analysis from first principles to validate the package and derive 289 // the types. We strictly prefer this to the gcexportdata. 290 if srcs, ok := i.sources[path]; ok && len(srcs) > 0 { 291 entry.pkg, entry.findings, entry.facts, entry.err = i.checkPackage(path, srcs) 292 if entry.err != nil { 293 return nil, entry.err 294 } 295 i.importsMu.Lock() 296 defer i.importsMu.Unlock() 297 i.imports[path] = entry.pkg 298 return entry.pkg, entry.err 299 } 300 301 // Load all exported data. Unfortunately, we will have to hold the lock 302 // during this time. The imported may access imports directly. 303 rc, err := i.findBinary(path) 304 if err != nil { 305 return nil, err 306 } 307 defer rc.Close() 308 r, err := gcexportdata.NewReader(rc) 309 if err != nil { 310 return nil, err 311 } 312 i.importsMu.Lock() 313 defer i.importsMu.Unlock() 314 entry.pkg, entry.err = gcexportdata.Read(r, i.fset, i.imports, path) 315 return entry.pkg, entry.err 316 } 317 318 // Import implements types.Importer.Import. 319 func (i *importer) Import(path string) (*types.Package, error) { 320 return i.importPackage(path) 321 } 322 323 // errorImporter tracks the last error. 324 type errorImporter struct { 325 *importer 326 lastErr error 327 } 328 329 // Import implements types.Importer.Import. 330 func (i *errorImporter) Import(path string) (*types.Package, error) { 331 pkg, err := i.importer.importPackage(path) 332 if err != nil { 333 i.lastErr = err 334 } 335 return pkg, err 336 } 337 338 // checkPackage is the backing implementation for CheckPackage and others. 339 // 340 // The implementation was adapted from [1], which was in turn adpated from [2]. 341 // This returns a list of matching analysis issues, or an error if the analysis 342 // could not be completed. 343 // 344 // Note that a partial result may be returned if an error occurred on at least 345 // one analyzer. This may be expected if e.g. a binary is not provided but a 346 // binaryAnalyzer is used. 347 // 348 // [1] bazelbuid/rules_go/tools/builders/nogo_main.go 349 // [2] golang.org/x/tools/go/checker/internal/checker 350 func (i *importer) checkPackage(path string, srcs []string) (*types.Package, FindingSet, *facts.Package, error) { 351 // Load all source files. 352 goFiles, nonGoFiles := sortSrcs(srcs) 353 syntax := make([]*ast.File, 0, len(goFiles)) 354 for _, file := range goFiles { 355 include, err := shouldInclude(file) 356 if err != nil { 357 return nil, nil, nil, fmt.Errorf("error evaluating file %q: %w", file, err) 358 } 359 if !include { 360 continue 361 } 362 s, err := parser.ParseFile(i.fset, file, nil, parser.ParseComments) 363 if err != nil { 364 return nil, nil, nil, fmt.Errorf("error parsing file %q: %w", file, err) 365 } 366 syntax = append(syntax, s) 367 } 368 otherFiles := make([]string, 0, len(nonGoFiles)) 369 for _, file := range nonGoFiles { 370 include, err := shouldInclude(file) 371 if err != nil { 372 return nil, nil, nil, fmt.Errorf("error evaluating non-Go file %q: %w", file, err) 373 } 374 if !include { 375 continue 376 } 377 otherFiles = append(otherFiles, file) 378 } 379 380 // Check type information. 381 ei := &errorImporter{ 382 importer: i, 383 } 384 typesSizes := types.SizesFor("gc", flags.GOARCH) 385 typeConfig := types.Config{ 386 Importer: ei, 387 Error: func(error) {}, 388 } 389 typesInfo := &types.Info{ 390 Types: make(map[ast.Expr]types.TypeAndValue), 391 Instances: make(map[*ast.Ident]types.Instance), 392 Uses: make(map[*ast.Ident]types.Object), 393 Defs: make(map[*ast.Ident]types.Object), 394 Implicits: make(map[ast.Node]types.Object), 395 Scopes: make(map[ast.Node]*types.Scope), 396 Selections: make(map[*ast.SelectorExpr]*types.Selection), 397 } 398 astPackage, err := typeConfig.Check(path, i.fset, syntax, typesInfo) 399 if err != nil && ei.lastErr != ErrSkip { 400 return nil, nil, nil, fmt.Errorf("error checking types: %w", err) 401 } 402 403 // Note that facts should be reconcilable between types as of go/tools 404 // commit ee04797aa0b6be5ce3d5f7ac0f91e34716b3acdf. We previously used 405 // to do a sanity check to ensure that binary import data was 406 // compatible with ast-derived data, but this is no longer necessary. 407 // If packages are available locally, we can refer to those directly. 408 astFacts := facts.NewPackage() 409 410 // Recursively visit all analyzers. 411 var ( 412 resultsMu sync.RWMutex // protects results & errs, findings. 413 factsMu sync.RWMutex // protects facts. 414 ready = make(map[*analysis.Analyzer]*sync.WaitGroup) 415 results = make(map[*analysis.Analyzer]any) 416 errs = make(map[*analysis.Analyzer]error) 417 findings = make(FindingSet, 0) 418 ) 419 for a := range allAnalyzers { 420 wg := new(sync.WaitGroup) 421 wg.Add(1) // For analysis. 422 ready[a] = wg 423 } 424 limit := make(chan struct{}, 1) 425 for a, wg := range ready { 426 go func(a *analysis.Analyzer, wg *sync.WaitGroup) { 427 defer wg.Done() 428 429 // Wait for all requirements. 430 for _, orig := range a.Requires { 431 ready[orig].Wait() 432 433 // Should we bail early? 434 resultsMu.RLock() 435 if err := errs[orig]; err != nil { 436 resultsMu.RUnlock() 437 resultsMu.Lock() 438 defer resultsMu.Unlock() 439 errs[a] = err 440 return 441 } 442 resultsMu.RUnlock() 443 } 444 445 limit <- struct{}{} 446 defer func() { <-limit }() 447 448 // Collect local fact types. 449 localFactTypes := make(map[reflect.Type]bool) 450 for _, ft := range a.FactTypes { 451 localFactTypes[reflect.TypeOf(ft)] = true 452 } 453 454 // Run the analysis. 455 var localFindings FindingSet 456 p := &analysis.Pass{ 457 Analyzer: a, 458 Fset: i.fset, 459 Files: syntax, 460 OtherFiles: otherFiles, 461 Pkg: astPackage, 462 TypesInfo: typesInfo, 463 ResultOf: results, // All results. 464 Report: func(d analysis.Diagnostic) { 465 localFindings = append(localFindings, Finding{ 466 Category: a.Name, 467 Position: i.fset.Position(d.Pos), 468 Message: d.Message, 469 GOOS: flags.GOOS, 470 GOARCH: flags.GOARCH, 471 }) 472 }, 473 ImportPackageFact: func(pkg *types.Package, ptr analysis.Fact) bool { 474 if pkg != astPackage { 475 if f := i.fastFacts(pkg); f != nil { 476 return f.ImportFact(nil, ptr) 477 } 478 return false 479 } 480 factsMu.RLock() 481 defer factsMu.RUnlock() 482 return astFacts.ImportFact(nil, ptr) 483 }, 484 ExportPackageFact: func(fact analysis.Fact) { 485 factsMu.Lock() 486 defer factsMu.Unlock() 487 astFacts.ExportFact(nil, fact) 488 }, 489 ImportObjectFact: func(obj types.Object, ptr analysis.Fact) bool { 490 if pkg := obj.Pkg(); pkg != nil && pkg != astPackage { 491 if f := i.fastFacts(pkg); f != nil { 492 return f.ImportFact(obj, ptr) 493 } 494 return false 495 } 496 factsMu.RLock() 497 defer factsMu.RUnlock() 498 return astFacts.ImportFact(obj, ptr) 499 }, 500 ExportObjectFact: func(obj types.Object, fact analysis.Fact) { 501 if obj == nil { 502 // Tried to export nil object? 503 return 504 } 505 if obj.Pkg() != astPackage { 506 // This is not allowed: the 507 // built-in facts library will 508 // also panic in this case. 509 return 510 } 511 factsMu.Lock() 512 defer factsMu.Unlock() 513 astFacts.ExportFact(obj, fact) 514 }, 515 AllPackageFacts: func() (rv []analysis.PackageFact) { 516 factsMu.RLock() 517 defer factsMu.RUnlock() 518 // Pull all dependencies. 519 for _, importedPkg := range astPackage.Imports() { 520 otherFacts := i.fastFacts(importedPkg) 521 if otherFacts == nil { 522 continue 523 } 524 for typ := range localFactTypes { 525 v := reflect.New(typ.Elem()) 526 if otherFacts.ImportFact(nil, v.Interface().(analysis.Fact)) { 527 rv = append(rv, analysis.PackageFact{ 528 Package: importedPkg, 529 Fact: v.Interface().(analysis.Fact), 530 }) 531 } 532 } 533 } 534 // Pull all local facts. 535 for typ := range localFactTypes { 536 v := reflect.New(typ.Elem()) 537 if astFacts.ImportFact(nil, v.Interface().(analysis.Fact)) { 538 rv = append(rv, analysis.PackageFact{ 539 Package: astPackage, 540 Fact: v.Interface().(analysis.Fact), 541 }) 542 } 543 } 544 return 545 }, 546 AllObjectFacts: func() (rv []analysis.ObjectFact) { 547 factsMu.RLock() 548 defer factsMu.RUnlock() 549 // Pull all local facts. 550 for obj := range astFacts.Objects { 551 for typ := range localFactTypes { 552 v := reflect.New(typ.Elem()) 553 if astFacts.ImportFact(obj, v.Interface().(analysis.Fact)) { 554 rv = append(rv, analysis.ObjectFact{ 555 Object: obj, 556 Fact: v.Interface().(analysis.Fact), 557 }) 558 } 559 } 560 } 561 return 562 }, 563 TypesSizes: typesSizes, 564 } 565 566 // Ensure any analyzer panics are captured. This may 567 // happen for packages that are not supported by 568 // specific analyzers. The only panic that can happen 569 // is while resultsMu is held as a read-only lock. 570 var ( 571 result any 572 err error 573 ) 574 defer func() { 575 if r := recover(); r != nil { 576 // In order to make the multiple 577 // analyzers running concurrently 578 // debuggable, capture panic exceptions 579 // and propagate as an analyzer error. 580 err = fmt.Errorf("panic recovered: %s (%s)", r, debug.Stack()) 581 resultsMu.RUnlock() // +checklocksignore 582 } 583 resultsMu.Lock() 584 findings = append(findings, localFindings...) 585 results[a] = result 586 errs[a] = err 587 resultsMu.Unlock() 588 }() 589 found := findAnalyzer(a) 590 resultsMu.RLock() 591 if ba, ok := found.(binaryAnalyzer); ok { 592 // Load the binary and analyze. 593 rc, loadErr := i.findArchive(path) 594 if loadErr != nil { 595 if loadErr != ErrSkip { 596 err = loadErr 597 } else { 598 err = nil // Ignore. 599 } 600 } else { 601 result, err = ba.Run(p, rc) 602 rc.Close() 603 } 604 } else { 605 result, err = a.Run(p) 606 } 607 resultsMu.RUnlock() 608 }(a, wg) 609 } 610 for _, wg := range ready { 611 // Wait for completion. 612 wg.Wait() 613 } 614 for a := range ready { 615 // Check the error. If we generate an error here, we report 616 // this as a finding that can be suppressed. Some analyzers 617 // will fail on some packages. 618 if errs[a] != nil { 619 filename := "" 620 if len(srcs) > 0 { 621 filename = srcs[0] 622 } 623 findings = append(findings, Finding{ 624 Category: a.Name, 625 Position: token.Position{Filename: filename}, 626 Message: errs[a].Error(), 627 GOOS: flags.GOOS, 628 GOARCH: flags.GOARCH, 629 }) 630 continue 631 } 632 633 // Check the result. Per above, we check that the type is what 634 // we expected and that an error did not occur during analysis. 635 if got, want := reflect.TypeOf(results[a]), a.ResultType; got != want { 636 return astPackage, findings, astFacts, fmt.Errorf("error: analyzer %s returned %v (expected type %v)", a.Name, results[a], want) 637 } 638 } 639 640 // Return all findings. 641 return astPackage, findings, astFacts, nil 642 } 643 644 // Package runs all analyzer on a single package. 645 func Package(path string, srcs []string) (FindingSet, facts.Serializer, error) { 646 i := &importer{ 647 fset: token.NewFileSet(), 648 cache: make(map[string]*importerEntry), 649 imports: make(map[string]*types.Package), 650 } 651 652 // See comment on internalPackages. 653 for _, pkg := range internalPackages { 654 if _, err := i.Import(pkg); err != nil { 655 return nil, nil, fmt.Errorf("error importing %s: %w", pkg, err) 656 } 657 } 658 659 _, findings, facts, err := i.checkPackage(path, srcs) 660 if err != nil { 661 return nil, nil, err 662 } 663 return findings, facts, nil 664 } 665 666 // allFactsAndFindings returns all factsAndFindings from an importer. 667 func (i *importer) allFactsAndFindings() (FindingSet, *facts.Bundle) { 668 var ( 669 findings = make(FindingSet, 0) 670 allFacts = facts.NewBundle() 671 ) 672 for path, entry := range i.cache { 673 findings = append(findings, entry.findings...) 674 allFacts.Add(path, entry.facts) 675 } 676 return findings, allFacts 677 } 678 679 // FindRoot finds a package root. 680 func FindRoot(srcs []string, srcRootRegex string) (string, error) { 681 if srcRootRegex == "" { 682 return "", nil 683 } 684 685 // Calculate the root source directory. This is always a directory 686 // named 'src', of which we simply take the first we find. This is a 687 // bit fragile, but works for all currently known Go source 688 // configurations. 689 // 690 // Note that there may be extra files outside of the root source 691 // directory; we simply ignore those. 692 re, err := regexp.Compile(srcRootRegex) 693 if err != nil { 694 return "", fmt.Errorf("srcRootRegex is not valid: %w", err) 695 } 696 srcRootPrefix := "" 697 for _, filename := range srcs { 698 if s := re.FindString(filename); len(s) > len(srcRootPrefix) { 699 srcRootPrefix = s 700 } 701 } 702 if srcRootPrefix == "" { 703 // For whatever reason, we didn't identify a good common prefix to use here. 704 return "", fmt.Errorf("unable to identify src prefix for %v with regex %s", srcs, srcRootRegex) 705 } 706 return srcRootPrefix, nil 707 } 708 709 // SplitPackages splits a typical package structure into packages. 710 func SplitPackages(srcs []string, srcRootPrefix string) map[string][]string { 711 sources := make(map[string][]string) 712 for _, filename := range srcs { 713 if !strings.HasPrefix(filename, srcRootPrefix) { 714 continue // Superflouous file. 715 } 716 d := path.Dir(filename) 717 if len(srcRootPrefix) >= len(d) { 718 continue // Not a file. 719 } 720 pkg := d[len(srcRootPrefix):] 721 for len(pkg) > 0 && pkg[0] == '/' { 722 pkg = pkg[1:] 723 } 724 if len(pkg) == 0 { 725 continue // Also not a file. 726 } 727 728 // Ignore any files with /testdata/ in the path. 729 if strings.Contains(filename, "/testdata/") { 730 continue 731 } 732 733 // Ignore all test files since they *may* be in a different 734 // package than the rest of the sources. 735 if strings.HasSuffix(filename, "_test.go") { 736 continue 737 } 738 739 // Skip the "builtin" package, which is only for docs and not a 740 // real package. Attempting type checking goes crazy. 741 if pkg == "builtin" { 742 continue 743 } 744 745 // In Go's sources, vendored packages under cmd/vendor are imported via 746 // paths not containing cmd/vendor. 747 pkg = strings.TrimPrefix(pkg, "cmd/vendor/") 748 749 // Place the special runtime package (functions emitted by the 750 // compiler itself) into the runtime packages. 751 if strings.Contains(filename, "cmd/compile/internal/typecheck/_builtin/runtime.go") { 752 pkg = "runtime" 753 } 754 755 // Add to the package. 756 sources[pkg] = append(sources[pkg], filename) 757 } 758 759 return sources 760 } 761 762 // Bundle checks a bundle of files (typically the standard library). 763 func Bundle(sources map[string][]string) (FindingSet, facts.Serializer, error) { 764 // Process all packages. 765 i := &importer{ 766 fset: token.NewFileSet(), 767 sources: sources, 768 cache: make(map[string]*importerEntry), 769 imports: make(map[string]*types.Package), 770 } 771 for pkg := range sources { 772 // Was there an error processing this package? 773 if _, err := i.importPackage(pkg); err != nil && err != ErrSkip { 774 return nil, nil, err 775 } 776 } 777 778 findings, facts := i.allFactsAndFindings() 779 return findings, facts, nil 780 }