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