| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package cache |
| |
| import ( |
| "context" |
| "go/ast" |
| "sync" |
| |
| "golang.org/x/tools/internal/lsp/source" |
| "golang.org/x/tools/internal/lsp/telemetry" |
| "golang.org/x/tools/internal/span" |
| errors "golang.org/x/xerrors" |
| ) |
| |
| // goFile holds all of the information we know about a Go file. |
| type goFile struct { |
| fileBase |
| |
| // mu protects all mutable state of the Go file, |
| // which can be modified during type-checking. |
| mu sync.Mutex |
| |
| // missingImports is the set of unresolved imports for this package. |
| // It contains any packages with `go list` errors. |
| missingImports map[packagePath]struct{} |
| |
| imports []*ast.ImportSpec |
| } |
| |
| type packageKey struct { |
| id packageID |
| mode source.ParseMode |
| } |
| |
| func (f *goFile) CheckPackageHandles(ctx context.Context) (cphs []source.CheckPackageHandle, err error) { |
| ctx = telemetry.File.With(ctx, f.URI()) |
| fh := f.Handle(ctx) |
| |
| cphs = f.isDirty(ctx, fh) |
| if len(cphs) == 0 { |
| cphs, err = f.view.loadParseTypecheck(ctx, f, fh) |
| if err != nil { |
| return nil, err |
| } |
| } |
| if len(cphs) == 0 { |
| return nil, errors.Errorf("no CheckPackageHandles for %s", f.URI()) |
| } |
| return cphs, nil |
| } |
| |
| func (v *view) GetActiveReverseDeps(ctx context.Context, uri span.URI) (results []source.CheckPackageHandle) { |
| var ( |
| rdeps = v.reverseDependencies(ctx, uri) |
| files = v.openFiles(ctx, rdeps) |
| seen = make(map[span.URI]struct{}) |
| ) |
| for _, f := range files { |
| if _, ok := seen[f.URI()]; ok { |
| continue |
| } |
| gof, ok := f.(source.GoFile) |
| if !ok { |
| continue |
| } |
| cphs, err := gof.CheckPackageHandles(ctx) |
| if err != nil { |
| continue |
| } |
| cph := source.WidestCheckPackageHandle(cphs) |
| for _, ph := range cph.Files() { |
| seen[ph.File().Identity().URI] = struct{}{} |
| } |
| results = append(results, cph) |
| } |
| return results |
| } |
| |
| // isDirty is true if the file needs to be type-checked. |
| // It assumes that the file's view's mutex is held by the caller. |
| func (f *goFile) isDirty(ctx context.Context, fh source.FileHandle) []source.CheckPackageHandle { |
| meta, cphs := f.view.getSnapshot(f.URI()) |
| if len(meta) == 0 { |
| return nil |
| } |
| var results []source.CheckPackageHandle |
| for key, cph := range cphs { |
| // If we're explicitly checking if a file needs to be type-checked, |
| // we need it to be fully parsed. |
| if key.mode != source.ParseFull { |
| continue |
| } |
| // Check if there is a fully-parsed package to which this file belongs. |
| for _, file := range cph.Files() { |
| if file.File().Identity() == fh.Identity() { |
| results = append(results, cph) |
| } |
| } |
| } |
| return results |
| } |