github.com/v2fly/tools@v0.100.0/internal/lsp/cache/check.go (about) 1 // Copyright 2019 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 package cache 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "go/ast" 12 "go/scanner" 13 "go/types" 14 "path" 15 "path/filepath" 16 "sort" 17 "strings" 18 "sync" 19 20 "github.com/v2fly/tools/go/ast/astutil" 21 "github.com/v2fly/tools/go/packages" 22 "github.com/v2fly/tools/internal/event" 23 "github.com/v2fly/tools/internal/lsp/debug/tag" 24 "github.com/v2fly/tools/internal/lsp/protocol" 25 "github.com/v2fly/tools/internal/lsp/source" 26 "github.com/v2fly/tools/internal/memoize" 27 "github.com/v2fly/tools/internal/packagesinternal" 28 "github.com/v2fly/tools/internal/span" 29 "github.com/v2fly/tools/internal/typesinternal" 30 "golang.org/x/mod/module" 31 errors "golang.org/x/xerrors" 32 ) 33 34 type packageHandleKey string 35 36 type packageHandle struct { 37 handle *memoize.Handle 38 39 goFiles, compiledGoFiles []*parseGoHandle 40 41 // mode is the mode the files were parsed in. 42 mode source.ParseMode 43 44 // m is the metadata associated with the package. 45 m *metadata 46 47 // key is the hashed key for the package. 48 key packageHandleKey 49 } 50 51 func (ph *packageHandle) packageKey() packageKey { 52 return packageKey{ 53 id: ph.m.id, 54 mode: ph.mode, 55 } 56 } 57 58 func (ph *packageHandle) imports(ctx context.Context, s source.Snapshot) (result []string) { 59 for _, pgh := range ph.goFiles { 60 f, err := s.ParseGo(ctx, pgh.file, source.ParseHeader) 61 if err != nil { 62 continue 63 } 64 seen := map[string]struct{}{} 65 for _, impSpec := range f.File.Imports { 66 imp := strings.Trim(impSpec.Path.Value, `"`) 67 if _, ok := seen[imp]; !ok { 68 seen[imp] = struct{}{} 69 result = append(result, imp) 70 } 71 } 72 } 73 74 sort.Strings(result) 75 return result 76 } 77 78 // packageData contains the data produced by type-checking a package. 79 type packageData struct { 80 pkg *pkg 81 err error 82 } 83 84 // buildPackageHandle returns a packageHandle for a given package and mode. 85 func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) { 86 if ph := s.getPackage(id, mode); ph != nil { 87 return ph, nil 88 } 89 90 // Build the packageHandle for this ID and its dependencies. 91 ph, deps, err := s.buildKey(ctx, id, mode) 92 if err != nil { 93 return nil, err 94 } 95 96 // Do not close over the packageHandle or the snapshot in the Bind function. 97 // This creates a cycle, which causes the finalizers to never run on the handles. 98 // The possible cycles are: 99 // 100 // packageHandle.h.function -> packageHandle 101 // packageHandle.h.function -> snapshot -> packageHandle 102 // 103 104 m := ph.m 105 key := ph.key 106 107 h := s.generation.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} { 108 snapshot := arg.(*snapshot) 109 110 // Begin loading the direct dependencies, in parallel. 111 var wg sync.WaitGroup 112 for _, dep := range deps { 113 wg.Add(1) 114 go func(dep *packageHandle) { 115 dep.check(ctx, snapshot) 116 wg.Done() 117 }(dep) 118 } 119 120 data := &packageData{} 121 data.pkg, data.err = typeCheck(ctx, snapshot, m, mode, deps) 122 // Make sure that the workers above have finished before we return, 123 // especially in case of cancellation. 124 wg.Wait() 125 126 return data 127 }, nil) 128 ph.handle = h 129 130 // Cache the handle in the snapshot. If a package handle has already 131 // been cached, addPackage will return the cached value. This is fine, 132 // since the original package handle above will have no references and be 133 // garbage collected. 134 ph = s.addPackageHandle(ph) 135 136 return ph, nil 137 } 138 139 // buildKey computes the key for a given packageHandle. 140 func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) { 141 m := s.getMetadata(id) 142 if m == nil { 143 return nil, nil, errors.Errorf("no metadata for %s", id) 144 } 145 goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode) 146 if err != nil { 147 return nil, nil, err 148 } 149 compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode) 150 if err != nil { 151 return nil, nil, err 152 } 153 ph := &packageHandle{ 154 m: m, 155 goFiles: goFiles, 156 compiledGoFiles: compiledGoFiles, 157 mode: mode, 158 } 159 // Make sure all of the depList are sorted. 160 depList := append([]packageID{}, m.deps...) 161 sort.Slice(depList, func(i, j int) bool { 162 return depList[i] < depList[j] 163 }) 164 165 deps := make(map[packagePath]*packageHandle) 166 167 // Begin computing the key by getting the depKeys for all dependencies. 168 var depKeys []packageHandleKey 169 for _, depID := range depList { 170 depHandle, err := s.buildPackageHandle(ctx, depID, s.workspaceParseMode(depID)) 171 if err != nil { 172 event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", id, depID), err, tag.Snapshot.Of(s.id)) 173 if ctx.Err() != nil { 174 return nil, nil, ctx.Err() 175 } 176 // One bad dependency should not prevent us from checking the entire package. 177 // Add a special key to mark a bad dependency. 178 depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id))) 179 continue 180 } 181 deps[depHandle.m.pkgPath] = depHandle 182 depKeys = append(depKeys, depHandle.key) 183 } 184 experimentalKey := s.View().Options().ExperimentalPackageCacheKey 185 ph.key = checkPackageKey(ph.m.id, compiledGoFiles, m.config, depKeys, mode, experimentalKey) 186 return ph, deps, nil 187 } 188 189 func (s *snapshot) workspaceParseMode(id packageID) source.ParseMode { 190 s.mu.Lock() 191 defer s.mu.Unlock() 192 _, ws := s.workspacePackages[id] 193 if !ws { 194 return source.ParseExported 195 } 196 if s.view.Options().MemoryMode == source.ModeNormal { 197 return source.ParseFull 198 } 199 200 // Degraded mode. Check for open files. 201 m, ok := s.metadata[id] 202 if !ok { 203 return source.ParseExported 204 } 205 for _, cgf := range m.compiledGoFiles { 206 if s.isOpenLocked(cgf) { 207 return source.ParseFull 208 } 209 } 210 return source.ParseExported 211 } 212 213 func checkPackageKey(id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey, mode source.ParseMode, experimentalKey bool) packageHandleKey { 214 b := bytes.NewBuffer(nil) 215 b.WriteString(string(id)) 216 if !experimentalKey { 217 // cfg was used to produce the other hashed inputs (package ID, parsed Go 218 // files, and deps). It should not otherwise affect the inputs to the type 219 // checker, so this experiment omits it. This should increase cache hits on 220 // the daemon as cfg contains the environment and working directory. 221 b.WriteString(hashConfig(cfg)) 222 } 223 b.WriteByte(byte(mode)) 224 for _, dep := range deps { 225 b.WriteString(string(dep)) 226 } 227 for _, cgf := range pghs { 228 b.WriteString(cgf.file.FileIdentity().String()) 229 } 230 return packageHandleKey(hashContents(b.Bytes())) 231 } 232 233 // hashEnv returns a hash of the snapshot's configuration. 234 func hashEnv(s *snapshot) string { 235 s.view.optionsMu.Lock() 236 env := s.view.options.EnvSlice() 237 s.view.optionsMu.Unlock() 238 239 b := &bytes.Buffer{} 240 for _, e := range env { 241 b.WriteString(e) 242 } 243 return hashContents(b.Bytes()) 244 } 245 246 // hashConfig returns the hash for the *packages.Config. 247 func hashConfig(config *packages.Config) string { 248 b := bytes.NewBuffer(nil) 249 250 // Dir, Mode, Env, BuildFlags are the parts of the config that can change. 251 b.WriteString(config.Dir) 252 b.WriteString(string(rune(config.Mode))) 253 254 for _, e := range config.Env { 255 b.WriteString(e) 256 } 257 for _, f := range config.BuildFlags { 258 b.WriteString(f) 259 } 260 return hashContents(b.Bytes()) 261 } 262 263 func (ph *packageHandle) Check(ctx context.Context, s source.Snapshot) (source.Package, error) { 264 return ph.check(ctx, s.(*snapshot)) 265 } 266 267 func (ph *packageHandle) check(ctx context.Context, s *snapshot) (*pkg, error) { 268 v, err := ph.handle.Get(ctx, s.generation, s) 269 if err != nil { 270 return nil, err 271 } 272 data := v.(*packageData) 273 return data.pkg, data.err 274 } 275 276 func (ph *packageHandle) CompiledGoFiles() []span.URI { 277 return ph.m.compiledGoFiles 278 } 279 280 func (ph *packageHandle) ID() string { 281 return string(ph.m.id) 282 } 283 284 func (ph *packageHandle) cached(g *memoize.Generation) (*pkg, error) { 285 v := ph.handle.Cached(g) 286 if v == nil { 287 return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath) 288 } 289 data := v.(*packageData) 290 return data.pkg, data.err 291 } 292 293 func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]*parseGoHandle, error) { 294 pghs := make([]*parseGoHandle, 0, len(files)) 295 for _, uri := range files { 296 fh, err := s.GetFile(ctx, uri) 297 if err != nil { 298 return nil, err 299 } 300 pghs = append(pghs, s.parseGoHandle(ctx, fh, mode)) 301 } 302 return pghs, nil 303 } 304 305 func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source.ParseMode, deps map[packagePath]*packageHandle) (*pkg, error) { 306 ctx, done := event.Start(ctx, "cache.importer.typeCheck", tag.Package.Of(string(m.id))) 307 defer done() 308 309 fset := snapshot.view.session.cache.fset 310 pkg := &pkg{ 311 m: m, 312 mode: mode, 313 goFiles: make([]*source.ParsedGoFile, len(m.goFiles)), 314 compiledGoFiles: make([]*source.ParsedGoFile, len(m.compiledGoFiles)), 315 imports: make(map[packagePath]*pkg), 316 typesSizes: m.typesSizes, 317 typesInfo: &types.Info{ 318 Types: make(map[ast.Expr]types.TypeAndValue), 319 Defs: make(map[*ast.Ident]types.Object), 320 Uses: make(map[*ast.Ident]types.Object), 321 Implicits: make(map[ast.Node]types.Object), 322 Selections: make(map[*ast.SelectorExpr]*types.Selection), 323 Scopes: make(map[ast.Node]*types.Scope), 324 }, 325 } 326 // If this is a replaced module in the workspace, the version is 327 // meaningless, and we don't want clients to access it. 328 if m.module != nil { 329 version := m.module.Version 330 if source.IsWorkspaceModuleVersion(version) { 331 version = "" 332 } 333 pkg.version = &module.Version{ 334 Path: m.module.Path, 335 Version: version, 336 } 337 } 338 var ( 339 files = make([]*ast.File, len(m.compiledGoFiles)) 340 parseErrors = make([]scanner.ErrorList, len(m.compiledGoFiles)) 341 actualErrors = make([]error, len(m.compiledGoFiles)) 342 wg sync.WaitGroup 343 344 mu sync.Mutex 345 haveFixedFiles bool 346 ) 347 for i, cgf := range m.compiledGoFiles { 348 wg.Add(1) 349 go func(i int, cgf span.URI) { 350 defer wg.Done() 351 fh, err := snapshot.GetFile(ctx, cgf) 352 if err != nil { 353 actualErrors[i] = err 354 return 355 } 356 pgh := snapshot.parseGoHandle(ctx, fh, mode) 357 pgf, fixed, err := snapshot.parseGo(ctx, pgh) 358 if err != nil { 359 actualErrors[i] = err 360 return 361 } 362 pkg.compiledGoFiles[i] = pgf 363 files[i], parseErrors[i], actualErrors[i] = pgf.File, pgf.ParseErr, err 364 365 // If we have fixed parse errors in any of the files, we should hide type 366 // errors, as they may be completely nonsensical. 367 mu.Lock() 368 haveFixedFiles = haveFixedFiles || fixed 369 mu.Unlock() 370 }(i, cgf) 371 } 372 for i, gf := range m.goFiles { 373 wg.Add(1) 374 // We need to parse the non-compiled go files, but we don't care about their errors. 375 go func(i int, gf span.URI) { 376 defer wg.Done() 377 fh, err := snapshot.GetFile(ctx, gf) 378 if err != nil { 379 return 380 } 381 pgf, _ := snapshot.ParseGo(ctx, fh, mode) 382 pkg.goFiles[i] = pgf 383 }(i, gf) 384 } 385 wg.Wait() 386 for _, err := range actualErrors { 387 if err != nil { 388 return nil, err 389 } 390 } 391 392 var i int 393 for _, e := range parseErrors { 394 if e != nil { 395 parseErrors[i] = e 396 i++ 397 } 398 } 399 parseErrors = parseErrors[:i] 400 401 i = 0 402 for _, f := range files { 403 if f != nil { 404 files[i] = f 405 i++ 406 } 407 } 408 files = files[:i] 409 410 // Use the default type information for the unsafe package. 411 if pkg.m.pkgPath == "unsafe" { 412 pkg.types = types.Unsafe 413 // Don't type check Unsafe: it's unnecessary, and doing so exposes a data 414 // race to Unsafe.completed. 415 return pkg, nil 416 } else if len(files) == 0 { // not the unsafe package, no parsed files 417 // Try to attach error messages to the file as much as possible. 418 var found bool 419 for _, e := range m.errors { 420 srcDiags, err := goPackagesErrorDiagnostics(snapshot, pkg, e) 421 if err != nil { 422 continue 423 } 424 found = true 425 pkg.diagnostics = append(pkg.diagnostics, srcDiags...) 426 } 427 if found { 428 return pkg, nil 429 } 430 return nil, errors.Errorf("no parsed files for package %s, expected: %v, errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, m.errors) 431 } else { 432 pkg.types = types.NewPackage(string(m.pkgPath), string(m.name)) 433 } 434 435 var typeErrors []types.Error 436 cfg := &types.Config{ 437 Error: func(e error) { 438 typeErrors = append(typeErrors, e.(types.Error)) 439 }, 440 Importer: importerFunc(func(pkgPath string) (*types.Package, error) { 441 // If the context was cancelled, we should abort. 442 if ctx.Err() != nil { 443 return nil, ctx.Err() 444 } 445 dep := resolveImportPath(pkgPath, pkg, deps) 446 if dep == nil { 447 return nil, snapshot.missingPkgError(pkgPath) 448 } 449 if !isValidImport(m.pkgPath, dep.m.pkgPath) { 450 return nil, errors.Errorf("invalid use of internal package %s", pkgPath) 451 } 452 depPkg, err := dep.check(ctx, snapshot) 453 if err != nil { 454 return nil, err 455 } 456 pkg.imports[depPkg.m.pkgPath] = depPkg 457 return depPkg.types, nil 458 }), 459 } 460 // We want to type check cgo code if go/types supports it. 461 // We passed typecheckCgo to go/packages when we Loaded. 462 typesinternal.SetUsesCgo(cfg) 463 464 check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo) 465 466 // Type checking errors are handled via the config, so ignore them here. 467 _ = check.Files(files) 468 // If the context was cancelled, we may have returned a ton of transient 469 // errors to the type checker. Swallow them. 470 if ctx.Err() != nil { 471 return nil, ctx.Err() 472 } 473 474 // We don't care about a package's errors unless we have parsed it in full. 475 if mode != source.ParseFull { 476 return pkg, nil 477 } 478 479 if len(m.errors) != 0 { 480 pkg.hasListOrParseErrors = true 481 for _, e := range m.errors { 482 diags, err := goPackagesErrorDiagnostics(snapshot, pkg, e) 483 if err != nil { 484 event.Error(ctx, "unable to compute positions for list errors", err, tag.Package.Of(pkg.ID())) 485 continue 486 } 487 pkg.diagnostics = append(pkg.diagnostics, diags...) 488 } 489 } 490 491 // Our heuristic for whether to show type checking errors is: 492 // + If any file was 'fixed', don't show type checking errors as we 493 // can't guarantee that they reference accurate locations in the source. 494 // + If there is a parse error _in the current file_, suppress type 495 // errors in that file. 496 // + Otherwise, show type errors even in the presence of parse errors in 497 // other package files. go/types attempts to suppress follow-on errors 498 // due to bad syntax, so on balance type checking errors still provide 499 // a decent signal/noise ratio as long as the file in question parses. 500 501 // Track URIs with parse errors so that we can suppress type errors for these 502 // files. 503 unparseable := map[span.URI]bool{} 504 if len(parseErrors) != 0 { 505 pkg.hasListOrParseErrors = true 506 for _, e := range parseErrors { 507 diags, err := parseErrorDiagnostics(snapshot, pkg, e) 508 if err != nil { 509 event.Error(ctx, "unable to compute positions for parse errors", err, tag.Package.Of(pkg.ID())) 510 continue 511 } 512 for _, diag := range diags { 513 unparseable[diag.URI] = true 514 pkg.diagnostics = append(pkg.diagnostics, diag) 515 } 516 } 517 } 518 519 if haveFixedFiles { 520 return pkg, nil 521 } 522 523 for _, e := range expandErrors(typeErrors, snapshot.View().Options().RelatedInformationSupported) { 524 pkg.hasTypeErrors = true 525 diags, err := typeErrorDiagnostics(snapshot, pkg, e) 526 if err != nil { 527 event.Error(ctx, "unable to compute positions for type errors", err, tag.Package.Of(pkg.ID())) 528 continue 529 } 530 pkg.typeErrors = append(pkg.typeErrors, e.primary) 531 for _, diag := range diags { 532 // If the file didn't parse cleanly, it is highly likely that type 533 // checking errors will be confusing or redundant. But otherwise, type 534 // checking usually provides a good enough signal to include. 535 if !unparseable[diag.URI] { 536 pkg.diagnostics = append(pkg.diagnostics, diag) 537 } 538 } 539 } 540 541 depsErrors, err := snapshot.depsErrors(ctx, pkg) 542 if err != nil { 543 return nil, err 544 } 545 pkg.diagnostics = append(pkg.diagnostics, depsErrors...) 546 547 return pkg, nil 548 } 549 550 func (s *snapshot) depsErrors(ctx context.Context, pkg *pkg) ([]*source.Diagnostic, error) { 551 // Select packages that can't be found, and were imported in non-workspace packages. 552 // Workspace packages already show their own errors. 553 var relevantErrors []*packagesinternal.PackageError 554 for _, depsError := range pkg.m.depsErrors { 555 // Up to Go 1.15, the missing package was included in the stack, which 556 // was presumably a bug. We want the next one up. 557 directImporterIdx := len(depsError.ImportStack) - 1 558 if s.view.goversion < 15 { 559 directImporterIdx = len(depsError.ImportStack) - 2 560 } 561 if directImporterIdx < 0 { 562 continue 563 } 564 565 directImporter := depsError.ImportStack[directImporterIdx] 566 if s.isWorkspacePackage(packageID(directImporter)) { 567 continue 568 } 569 relevantErrors = append(relevantErrors, depsError) 570 } 571 572 // Don't build the import index for nothing. 573 if len(relevantErrors) == 0 { 574 return nil, nil 575 } 576 577 // Build an index of all imports in the package. 578 type fileImport struct { 579 cgf *source.ParsedGoFile 580 imp *ast.ImportSpec 581 } 582 allImports := map[string][]fileImport{} 583 for _, cgf := range pkg.compiledGoFiles { 584 for _, group := range astutil.Imports(s.FileSet(), cgf.File) { 585 for _, imp := range group { 586 if imp.Path == nil { 587 continue 588 } 589 path := strings.Trim(imp.Path.Value, `"`) 590 allImports[path] = append(allImports[path], fileImport{cgf, imp}) 591 } 592 } 593 } 594 595 // Apply a diagnostic to any import involved in the error, stopping once 596 // we reach the workspace. 597 var errors []*source.Diagnostic 598 for _, depErr := range relevantErrors { 599 for i := len(depErr.ImportStack) - 1; i >= 0; i-- { 600 item := depErr.ImportStack[i] 601 if s.isWorkspacePackage(packageID(item)) { 602 break 603 } 604 605 for _, imp := range allImports[item] { 606 rng, err := source.NewMappedRange(s.FileSet(), imp.cgf.Mapper, imp.imp.Pos(), imp.imp.End()).Range() 607 if err != nil { 608 return nil, err 609 } 610 fixes, err := goGetQuickFixes(s, imp.cgf.URI, item) 611 if err != nil { 612 return nil, err 613 } 614 errors = append(errors, &source.Diagnostic{ 615 URI: imp.cgf.URI, 616 Range: rng, 617 Severity: protocol.SeverityError, 618 Source: source.TypeError, 619 Message: fmt.Sprintf("error while importing %v: %v", item, depErr.Err), 620 SuggestedFixes: fixes, 621 }) 622 } 623 } 624 } 625 626 if len(pkg.compiledGoFiles) == 0 { 627 return errors, nil 628 } 629 mod := s.GoModForFile(pkg.compiledGoFiles[0].URI) 630 if mod == "" { 631 return errors, nil 632 } 633 fh, err := s.GetFile(ctx, mod) 634 if err != nil { 635 return nil, err 636 } 637 pm, err := s.ParseMod(ctx, fh) 638 if err != nil { 639 return nil, err 640 } 641 642 // Add a diagnostic to the module that contained the lowest-level import of 643 // the missing package. 644 for _, depErr := range relevantErrors { 645 for i := len(depErr.ImportStack) - 1; i >= 0; i-- { 646 item := depErr.ImportStack[i] 647 m := s.getMetadata(packageID(item)) 648 if m == nil || m.module == nil { 649 continue 650 } 651 modVer := module.Version{Path: m.module.Path, Version: m.module.Version} 652 reference := findModuleReference(pm.File, modVer) 653 if reference == nil { 654 continue 655 } 656 rng, err := rangeFromPositions(pm.Mapper, reference.Start, reference.End) 657 if err != nil { 658 return nil, err 659 } 660 fixes, err := goGetQuickFixes(s, pm.URI, item) 661 if err != nil { 662 return nil, err 663 } 664 errors = append(errors, &source.Diagnostic{ 665 URI: pm.URI, 666 Range: rng, 667 Severity: protocol.SeverityError, 668 Source: source.TypeError, 669 Message: fmt.Sprintf("error while importing %v: %v", item, depErr.Err), 670 SuggestedFixes: fixes, 671 }) 672 break 673 } 674 } 675 return errors, nil 676 } 677 678 // missingPkgError returns an error message for a missing package that varies 679 // based on the user's workspace mode. 680 func (s *snapshot) missingPkgError(pkgPath string) error { 681 if s.workspaceMode()&moduleMode != 0 { 682 return fmt.Errorf("no required module provides package %q", pkgPath) 683 } 684 gorootSrcPkg := filepath.FromSlash(filepath.Join(s.view.goroot, "src", pkgPath)) 685 686 var b strings.Builder 687 b.WriteString(fmt.Sprintf("cannot find package %q in any of \n\t%s (from $GOROOT)", pkgPath, gorootSrcPkg)) 688 689 for _, gopath := range strings.Split(s.view.gopath, ":") { 690 gopathSrcPkg := filepath.FromSlash(filepath.Join(gopath, "src", pkgPath)) 691 b.WriteString(fmt.Sprintf("\n\t%s (from $GOPATH)", gopathSrcPkg)) 692 } 693 return errors.New(b.String()) 694 } 695 696 type extendedError struct { 697 primary types.Error 698 secondaries []types.Error 699 } 700 701 func (e extendedError) Error() string { 702 return e.primary.Error() 703 } 704 705 // expandErrors duplicates "secondary" errors by mapping them to their main 706 // error. Some errors returned by the type checker are followed by secondary 707 // errors which give more information about the error. These are errors in 708 // their own right, and they are marked by starting with \t. For instance, when 709 // there is a multiply-defined function, the secondary error points back to the 710 // definition first noticed. 711 // 712 // This function associates the secondary error with its primary error, which can 713 // then be used as RelatedInformation when the error becomes a diagnostic. 714 // 715 // If supportsRelatedInformation is false, the secondary is instead embedded as 716 // additional context in the primary error. 717 func expandErrors(errs []types.Error, supportsRelatedInformation bool) []extendedError { 718 var result []extendedError 719 for i := 0; i < len(errs); { 720 original := extendedError{ 721 primary: errs[i], 722 } 723 for i++; i < len(errs); i++ { 724 spl := errs[i] 725 if len(spl.Msg) == 0 || spl.Msg[0] != '\t' { 726 break 727 } 728 spl.Msg = spl.Msg[1:] 729 original.secondaries = append(original.secondaries, spl) 730 } 731 732 // Clone the error to all its related locations -- VS Code, at least, 733 // doesn't do it for us. 734 result = append(result, original) 735 for i, mainSecondary := range original.secondaries { 736 // Create the new primary error, with a tweaked message, in the 737 // secondary's location. We need to start from the secondary to 738 // capture its unexported location fields. 739 relocatedSecondary := mainSecondary 740 if supportsRelatedInformation { 741 relocatedSecondary.Msg = fmt.Sprintf("%v (see details)", original.primary.Msg) 742 } else { 743 relocatedSecondary.Msg = fmt.Sprintf("%v (this error: %v)", original.primary.Msg, mainSecondary.Msg) 744 } 745 relocatedSecondary.Soft = original.primary.Soft 746 747 // Copy over the secondary errors, noting the location of the 748 // current error we're cloning. 749 clonedError := extendedError{primary: relocatedSecondary, secondaries: []types.Error{original.primary}} 750 for j, secondary := range original.secondaries { 751 if i == j { 752 secondary.Msg += " (this error)" 753 } 754 clonedError.secondaries = append(clonedError.secondaries, secondary) 755 } 756 result = append(result, clonedError) 757 } 758 759 } 760 return result 761 } 762 763 // resolveImportPath resolves an import path in pkg to a package from deps. 764 // It should produce the same results as resolveImportPath: 765 // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990. 766 func resolveImportPath(importPath string, pkg *pkg, deps map[packagePath]*packageHandle) *packageHandle { 767 if dep := deps[packagePath(importPath)]; dep != nil { 768 return dep 769 } 770 // We may be in GOPATH mode, in which case we need to check vendor dirs. 771 searchDir := path.Dir(pkg.PkgPath()) 772 for { 773 vdir := packagePath(path.Join(searchDir, "vendor", importPath)) 774 if vdep := deps[vdir]; vdep != nil { 775 return vdep 776 } 777 778 // Search until Dir doesn't take us anywhere new, e.g. "." or "/". 779 next := path.Dir(searchDir) 780 if searchDir == next { 781 break 782 } 783 searchDir = next 784 } 785 786 // Vendor didn't work. Let's try minimal module compatibility mode. 787 // In MMC, the packagePath is the canonical (.../vN/...) path, which 788 // is hard to calculate. But the go command has already resolved the ID 789 // to the non-versioned path, and we can take advantage of that. 790 for _, dep := range deps { 791 if dep.ID() == importPath { 792 return dep 793 } 794 } 795 return nil 796 } 797 798 func isValidImport(pkgPath, importPkgPath packagePath) bool { 799 i := strings.LastIndex(string(importPkgPath), "/internal/") 800 if i == -1 { 801 return true 802 } 803 if isCommandLineArguments(string(pkgPath)) { 804 return true 805 } 806 return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i])) 807 } 808 809 // An importFunc is an implementation of the single-method 810 // types.Importer interface based on a function value. 811 type importerFunc func(path string) (*types.Package, error) 812 813 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }