| // 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" |
| "fmt" |
| "go/ast" |
| "go/scanner" |
| "go/token" |
| "go/types" |
| "sync" |
| |
| "golang.org/x/tools/gopls/internal/lsp/source" |
| "golang.org/x/tools/gopls/internal/lsp/source/methodsets" |
| "golang.org/x/tools/gopls/internal/lsp/source/xrefs" |
| "golang.org/x/tools/gopls/internal/span" |
| ) |
| |
| // Convenient local aliases for typed strings. |
| type ( |
| PackageID = source.PackageID |
| PackagePath = source.PackagePath |
| PackageName = source.PackageName |
| ImportPath = source.ImportPath |
| ) |
| |
| // A Package is the union of package metadata and type checking results. |
| // |
| // TODO(rfindley): for now, we do not persist the post-processing of |
| // loadDiagnostics, because the value of the snapshot.packages map is just the |
| // package handle. Fix this. |
| type Package struct { |
| m *source.Metadata |
| pkg *syntaxPackage |
| } |
| |
| // syntaxPackage contains parse trees and type information for a package. |
| type syntaxPackage struct { |
| // -- identifiers -- |
| id PackageID |
| |
| // -- outputs -- |
| fset *token.FileSet // for now, same as the snapshot's FileSet |
| goFiles []*source.ParsedGoFile |
| compiledGoFiles []*source.ParsedGoFile |
| diagnostics []*source.Diagnostic |
| parseErrors []scanner.ErrorList |
| typeErrors []types.Error |
| types *types.Package |
| typesInfo *types.Info |
| importMap map[PackagePath]*types.Package |
| hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors |
| |
| xrefsOnce sync.Once |
| _xrefs []byte // only used by the xrefs method |
| |
| methodsetsOnce sync.Once |
| _methodsets *methodsets.Index // only used by the methodsets method |
| } |
| |
| func (p *syntaxPackage) xrefs() []byte { |
| p.xrefsOnce.Do(func() { |
| p._xrefs = xrefs.Index(p.compiledGoFiles, p.types, p.typesInfo) |
| }) |
| return p._xrefs |
| } |
| |
| func (p *syntaxPackage) methodsets() *methodsets.Index { |
| p.methodsetsOnce.Do(func() { |
| p._methodsets = methodsets.NewIndex(p.fset, p.types) |
| }) |
| return p._methodsets |
| } |
| |
| func (p *Package) String() string { return string(p.m.ID) } |
| |
| func (p *Package) Metadata() *source.Metadata { return p.m } |
| |
| // A loadScope defines a package loading scope for use with go/packages. |
| // |
| // TODO(rfindley): move this to load.go. |
| type loadScope interface { |
| aScope() |
| } |
| |
| type ( |
| fileLoadScope span.URI // load packages containing a file (including command-line-arguments) |
| packageLoadScope string // load a specific package (the value is its PackageID) |
| moduleLoadScope struct { |
| dir string // dir containing the go.mod file |
| modulePath string // parsed module path |
| } |
| viewLoadScope span.URI // load the workspace |
| ) |
| |
| // Implement the loadScope interface. |
| func (fileLoadScope) aScope() {} |
| func (packageLoadScope) aScope() {} |
| func (moduleLoadScope) aScope() {} |
| func (viewLoadScope) aScope() {} |
| |
| func (p *Package) CompiledGoFiles() []*source.ParsedGoFile { |
| return p.pkg.compiledGoFiles |
| } |
| |
| func (p *Package) File(uri span.URI) (*source.ParsedGoFile, error) { |
| return p.pkg.File(uri) |
| } |
| |
| func (pkg *syntaxPackage) File(uri span.URI) (*source.ParsedGoFile, error) { |
| for _, cgf := range pkg.compiledGoFiles { |
| if cgf.URI == uri { |
| return cgf, nil |
| } |
| } |
| for _, gf := range pkg.goFiles { |
| if gf.URI == uri { |
| return gf, nil |
| } |
| } |
| return nil, fmt.Errorf("no parsed file for %s in %v", uri, pkg.id) |
| } |
| |
| func (p *Package) GetSyntax() []*ast.File { |
| var syntax []*ast.File |
| for _, pgf := range p.pkg.compiledGoFiles { |
| syntax = append(syntax, pgf.File) |
| } |
| return syntax |
| } |
| |
| func (p *Package) FileSet() *token.FileSet { |
| return p.pkg.fset |
| } |
| |
| func (p *Package) GetTypes() *types.Package { |
| return p.pkg.types |
| } |
| |
| func (p *Package) GetTypesInfo() *types.Info { |
| return p.pkg.typesInfo |
| } |
| |
| // DependencyTypes returns the type checker's symbol for the specified |
| // package. It returns nil if path is not among the transitive |
| // dependencies of p, or if no symbols from that package were |
| // referenced during the type-checking of p. |
| func (p *Package) DependencyTypes(path source.PackagePath) *types.Package { |
| return p.pkg.importMap[path] |
| } |
| |
| func (p *Package) GetParseErrors() []scanner.ErrorList { |
| return p.pkg.parseErrors |
| } |
| |
| func (p *Package) GetTypeErrors() []types.Error { |
| return p.pkg.typeErrors |
| } |
| |
| func (p *Package) DiagnosticsForFile(ctx context.Context, s source.Snapshot, uri span.URI) ([]*source.Diagnostic, error) { |
| var diags []*source.Diagnostic |
| for _, diag := range p.m.Diagnostics { |
| if diag.URI == uri { |
| diags = append(diags, diag) |
| } |
| } |
| for _, diag := range p.pkg.diagnostics { |
| if diag.URI == uri { |
| diags = append(diags, diag) |
| } |
| } |
| |
| return diags, nil |
| } |