blob: 37aae50a08750fea92ab053835b87df48185f1ea [file] [log] [blame]
// 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 (
"fmt"
"go/ast"
"go/scanner"
"go/token"
"go/types"
"golang.org/x/mod/module"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"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"
"golang.org/x/tools/internal/memoize"
)
// Convenient local aliases for typed strings.
type (
PackageID = source.PackageID
PackagePath = source.PackagePath
PackageName = source.PackageName
ImportPath = source.ImportPath
)
// pkg contains parse trees and type information for a package.
type pkg struct {
m *source.Metadata
mode source.ParseMode
fset *token.FileSet // for now, same as the snapshot's FileSet
goFiles []*source.ParsedGoFile
compiledGoFiles []*source.ParsedGoFile
diagnostics []*source.Diagnostic
deps map[PackageID]*pkg // use m.DepsBy{Pkg,Imp}Path to look up ID
version *module.Version // may be nil; may differ from m.Module.Version
parseErrors []scanner.ErrorList
typeErrors []types.Error
types *types.Package
typesInfo *types.Info
hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors
xrefs []byte // serializable index of outbound cross-references
analyses memoize.Store // maps analyzer.Name to Promise[actionResult]
methodsets *methodsets.Index // index of method sets of package-level types
}
func (p *pkg) String() string { return string(p.ID()) }
// A loadScope defines a package loading scope for use with go/packages.
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 string // load packages in a specific module
viewLoadScope span.URI // load the workspace
)
// Implement the loadScope interface.
func (fileLoadScope) aScope() {}
func (packageLoadScope) aScope() {}
func (moduleLoadScope) aScope() {}
func (viewLoadScope) aScope() {}
func (p *pkg) ID() PackageID { return p.m.ID }
func (p *pkg) Name() PackageName { return p.m.Name }
func (p *pkg) PkgPath() PackagePath { return p.m.PkgPath }
func (p *pkg) ParseMode() source.ParseMode {
return p.mode
}
func (p *pkg) CompiledGoFiles() []*source.ParsedGoFile {
return p.compiledGoFiles
}
func (p *pkg) File(uri span.URI) (*source.ParsedGoFile, error) {
for _, cgf := range p.compiledGoFiles {
if cgf.URI == uri {
return cgf, nil
}
}
for _, gf := range p.goFiles {
if gf.URI == uri {
return gf, nil
}
}
return nil, fmt.Errorf("no parsed file for %s in %v", uri, p.m.ID)
}
func (p *pkg) GetSyntax() []*ast.File {
var syntax []*ast.File
for _, pgf := range p.compiledGoFiles {
syntax = append(syntax, pgf.File)
}
return syntax
}
func (p *pkg) FileSet() *token.FileSet {
return p.fset
}
func (p *pkg) GetTypes() *types.Package {
return p.types
}
func (p *pkg) GetTypesInfo() *types.Info {
return p.typesInfo
}
func (p *pkg) GetTypesSizes() types.Sizes {
return p.m.TypesSizes
}
func (p *pkg) ForTest() string {
return string(p.m.ForTest)
}
// DirectDep returns the directly imported dependency of this package,
// given its PackagePath. (If you have an ImportPath, e.g. a string
// from an import declaration, use ResolveImportPath instead.
// They may differ in case of vendoring.)
func (p *pkg) DirectDep(pkgPath PackagePath) (source.Package, error) {
if id, ok := p.m.DepsByPkgPath[pkgPath]; ok {
if imp := p.deps[id]; imp != nil {
return imp, nil
}
}
return nil, fmt.Errorf("package does not import package with path %s", pkgPath)
}
// ResolveImportPath returns the directly imported dependency of this package,
// given its ImportPath. See also DirectDep.
func (p *pkg) ResolveImportPath(importPath ImportPath) (source.Package, error) {
if id, ok := p.m.DepsByImpPath[importPath]; ok && id != "" {
if imp := p.deps[id]; imp != nil {
return imp, nil
}
}
return nil, fmt.Errorf("package does not import %s", importPath)
}
func (p *pkg) Imports() []source.Package {
var result []source.Package // unordered
for _, dep := range p.deps {
result = append(result, dep)
}
return result
}
func (p *pkg) Version() *module.Version {
return p.version
}
func (p *pkg) HasListOrParseErrors() bool {
return len(p.m.Errors) != 0 || len(p.parseErrors) != 0
}
func (p *pkg) HasTypeErrors() bool {
return len(p.typeErrors) != 0
}
func (p *pkg) DiagnosticsForFile(uri span.URI) []*source.Diagnostic {
var res []*source.Diagnostic
for _, diag := range p.diagnostics {
if diag.URI == uri {
res = append(res, diag)
}
}
return res
}
// ReferencesTo returns the location of each reference within package p
// to one of the target objects denoted by the pair (package path, object path).
func (p *pkg) ReferencesTo(targets map[PackagePath]map[objectpath.Path]struct{}) []protocol.Location {
// TODO(adonovan): In future, p.xrefs will be retrieved from a
// section of the cache file produced by type checking.
// (Other sections will include the package's export data,
// "implements" relations, exported symbols, etc.)
// For now we just hang it off the pkg.
return xrefs.Lookup(p.m, p.xrefs, targets)
}
func (p *pkg) MethodSetsIndex() *methodsets.Index {
// TODO(adonovan): In future, p.methodsets will be retrieved from a
// section of the cache file produced by type checking.
return p.methodsets
}