aboutsummaryrefslogtreecommitdiff
path: root/gopls/internal/lsp/source/view.go
diff options
context:
space:
mode:
Diffstat (limited to 'gopls/internal/lsp/source/view.go')
-rw-r--r--gopls/internal/lsp/source/view.go857
1 files changed, 857 insertions, 0 deletions
diff --git a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go
new file mode 100644
index 000000000..41bcbac4b
--- /dev/null
+++ b/gopls/internal/lsp/source/view.go
@@ -0,0 +1,857 @@
+// Copyright 2018 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 source
+
+import (
+ "bytes"
+ "context"
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "io"
+
+ "golang.org/x/mod/modfile"
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/packages"
+ "golang.org/x/tools/go/types/objectpath"
+ "golang.org/x/tools/gopls/internal/govulncheck"
+ "golang.org/x/tools/gopls/internal/lsp/protocol"
+ "golang.org/x/tools/gopls/internal/lsp/safetoken"
+ "golang.org/x/tools/gopls/internal/lsp/source/methodsets"
+ "golang.org/x/tools/gopls/internal/span"
+ "golang.org/x/tools/internal/event/label"
+ "golang.org/x/tools/internal/event/tag"
+ "golang.org/x/tools/internal/gocommand"
+ "golang.org/x/tools/internal/imports"
+ "golang.org/x/tools/internal/packagesinternal"
+)
+
+// A GlobalSnapshotID uniquely identifies a snapshot within this process and
+// increases monotonically with snapshot creation time.
+//
+// We use a distinct integral type for global IDs to help enforce correct
+// usage.
+type GlobalSnapshotID uint64
+
+// Snapshot represents the current state for the given view.
+type Snapshot interface {
+ // SequenceID is the sequence id of this snapshot within its containing
+ // view.
+ //
+ // Relative to their view sequence ids are monotonically increasing, but this
+ // does not hold globally: when new views are created their initial snapshot
+ // has sequence ID 0. For operations that span multiple views, use global
+ // IDs.
+ SequenceID() uint64
+
+ // GlobalID is a globally unique identifier for this snapshot. Global IDs are
+ // monotonic: subsequent snapshots will have higher global ID, though
+ // subsequent snapshots in a view may not have adjacent global IDs.
+ GlobalID() GlobalSnapshotID
+
+ // View returns the View associated with this snapshot.
+ View() View
+
+ // BackgroundContext returns a context used for all background processing
+ // on behalf of this snapshot.
+ BackgroundContext() context.Context
+
+ // ValidBuildConfiguration returns true if there is some error in the
+ // user's workspace. In particular, if they are both outside of a module
+ // and their GOPATH.
+ ValidBuildConfiguration() bool
+
+ // FindFile returns the FileHandle for the given URI, if it is already
+ // in the given snapshot.
+ FindFile(uri span.URI) FileHandle
+
+ // GetFile returns the FileHandle for a given URI, initializing it if it is
+ // not already part of the snapshot.
+ GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
+
+ // AwaitInitialized waits until the snapshot's view is initialized.
+ AwaitInitialized(ctx context.Context)
+
+ // IsOpen returns whether the editor currently has a file open.
+ IsOpen(uri span.URI) bool
+
+ // IgnoredFile reports if a file would be ignored by a `go list` of the whole
+ // workspace.
+ IgnoredFile(uri span.URI) bool
+
+ // Templates returns the .tmpl files
+ Templates() map[span.URI]FileHandle
+
+ // ParseGo returns the parsed AST for the file.
+ // If the file is not available, returns nil and an error.
+ // Position information is added to FileSet().
+ ParseGo(ctx context.Context, fh FileHandle, mode ParseMode) (*ParsedGoFile, error)
+
+ // Analyze runs the specified analyzers on the given package at this snapshot.
+ Analyze(ctx context.Context, id PackageID, analyzers []*Analyzer) ([]*Diagnostic, error)
+
+ // RunGoCommandPiped runs the given `go` command, writing its output
+ // to stdout and stderr. Verb, Args, and WorkingDir must be specified.
+ //
+ // RunGoCommandPiped runs the command serially using gocommand.RunPiped,
+ // enforcing that this command executes exclusively to other commands on the
+ // server.
+ RunGoCommandPiped(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error
+
+ // RunGoCommandDirect runs the given `go` command. Verb, Args, and
+ // WorkingDir must be specified.
+ RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error)
+
+ // RunGoCommands runs a series of `go` commands that updates the go.mod
+ // and go.sum file for wd, and returns their updated contents.
+ RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error)
+
+ // RunProcessEnvFunc runs fn with the process env for this snapshot's view.
+ // Note: the process env contains cached module and filesystem state.
+ RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error
+
+ // ModFiles are the go.mod files enclosed in the snapshot's view and known
+ // to the snapshot.
+ ModFiles() []span.URI
+
+ // ParseMod is used to parse go.mod files.
+ ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error)
+
+ // ModWhy returns the results of `go mod why` for the module specified by
+ // the given go.mod file.
+ ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error)
+
+ // ModTidy returns the results of `go mod tidy` for the module specified by
+ // the given go.mod file.
+ ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error)
+
+ // ModVuln returns import vulnerability analysis for the given go.mod URI.
+ // Concurrent requests are combined into a single command.
+ ModVuln(ctx context.Context, modURI span.URI) (*govulncheck.Result, error)
+
+ // GoModForFile returns the URI of the go.mod file for the given URI.
+ GoModForFile(uri span.URI) span.URI
+
+ // WorkFile, if non-empty, is the go.work file for the workspace.
+ WorkFile() span.URI
+
+ // ParseWork is used to parse go.work files.
+ ParseWork(ctx context.Context, fh FileHandle) (*ParsedWorkFile, error)
+
+ // BuiltinFile returns information about the special builtin package.
+ BuiltinFile(ctx context.Context) (*ParsedGoFile, error)
+
+ // IsBuiltin reports whether uri is part of the builtin package.
+ IsBuiltin(ctx context.Context, uri span.URI) bool
+
+ // ReverseDependencies returns a new mapping whose entries are
+ // the ID and Metadata of each package in the workspace that
+ // directly or transitively depend on the package denoted by id,
+ // excluding id itself.
+ ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*Metadata, error)
+
+ // ActiveMetadata returns a new, unordered slice containing
+ // metadata for all packages considered 'active' in the workspace.
+ //
+ // In normal memory mode, this is all workspace packages. In degraded memory
+ // mode, this is just the reverse transitive closure of open packages.
+ ActiveMetadata(ctx context.Context) ([]*Metadata, error)
+
+ // AllMetadata returns a new unordered array of metadata for all packages in the workspace.
+ AllMetadata(ctx context.Context) ([]*Metadata, error)
+
+ // Symbols returns all symbols in the snapshot.
+ Symbols(ctx context.Context) (map[span.URI][]Symbol, error)
+
+ // Metadata returns the metadata for the specified package,
+ // or nil if it was not found.
+ Metadata(id PackageID) *Metadata
+
+ // MetadataForFile returns a new slice containing metadata for each
+ // package containing the Go file identified by uri, ordered by the
+ // number of CompiledGoFiles (i.e. "narrowest" to "widest" package).
+ // The result may include tests and intermediate test variants of
+ // importable packages.
+ // It returns an error if the context was cancelled.
+ MetadataForFile(ctx context.Context, uri span.URI) ([]*Metadata, error)
+
+ // TypeCheck parses and type-checks the specified packages,
+ // and returns them in the same order as the ids.
+ // The resulting packages' types may belong to different importers,
+ // so types from different packages are incommensurable.
+ TypeCheck(ctx context.Context, ids ...PackageID) ([]Package, error)
+
+ // PackageDiagnostics returns diagnostics for files contained in specified
+ // packages.
+ //
+ // If these diagnostics cannot be loaded from cache, the requested packages
+ // may be type-checked.
+ PackageDiagnostics(ctx context.Context, ids ...PackageID) (map[span.URI][]*Diagnostic, error)
+
+ // References returns cross-references indexes for the specified packages.
+ //
+ // If these indexes cannot be loaded from cache, the requested packages may
+ // be type-checked.
+ References(ctx context.Context, ids ...PackageID) ([]XrefIndex, error)
+
+ // MethodSets returns method-set indexes for the specified packages.
+ //
+ // If these indexes cannot be loaded from cache, the requested packages may
+ // be type-checked.
+ MethodSets(ctx context.Context, ids ...PackageID) ([]*methodsets.Index, error)
+
+ // GetCriticalError returns any critical errors in the workspace.
+ //
+ // A nil result may mean success, or context cancellation.
+ GetCriticalError(ctx context.Context) *CriticalError
+}
+
+type XrefIndex interface {
+ Lookup(targets map[PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location)
+}
+
+// SnapshotLabels returns a new slice of labels that should be used for events
+// related to a snapshot.
+func SnapshotLabels(snapshot Snapshot) []label.Label {
+ return []label.Label{tag.Snapshot.Of(snapshot.SequenceID()), tag.Directory.Of(snapshot.View().Folder())}
+}
+
+// PackageForFile is a convenience function that selects a package to
+// which this file belongs (narrowest or widest), type-checks it in
+// the requested mode (full or workspace), and returns it, along with
+// the parse tree of that file.
+//
+// Type-checking is expensive. Call snapshot.ParseGo if all you need
+// is a parse tree, or snapshot.MetadataForFile if you only need metadata.
+func PackageForFile(ctx context.Context, snapshot Snapshot, uri span.URI, pkgSel PackageSelector) (Package, *ParsedGoFile, error) {
+ metas, err := snapshot.MetadataForFile(ctx, uri)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(metas) == 0 {
+ return nil, nil, fmt.Errorf("no package metadata for file %s", uri)
+ }
+ switch pkgSel {
+ case NarrowestPackage:
+ metas = metas[:1]
+ case WidestPackage:
+ metas = metas[len(metas)-1:]
+ }
+ pkgs, err := snapshot.TypeCheck(ctx, metas[0].ID)
+ if err != nil {
+ return nil, nil, err
+ }
+ pkg := pkgs[0]
+ pgf, err := pkg.File(uri)
+ if err != nil {
+ return nil, nil, err // "can't happen"
+ }
+ return pkg, pgf, err
+}
+
+// PackageSelector sets how a package is selected out from a set of packages
+// containing a given file.
+type PackageSelector int
+
+const (
+ // NarrowestPackage picks the "narrowest" package for a given file.
+ // By "narrowest" package, we mean the package with the fewest number of
+ // files that includes the given file. This solves the problem of test
+ // variants, as the test will have more files than the non-test package.
+ NarrowestPackage PackageSelector = iota
+
+ // WidestPackage returns the Package containing the most files.
+ // This is useful for something like diagnostics, where we'd prefer to
+ // offer diagnostics for as many files as possible.
+ WidestPackage
+)
+
+// InvocationFlags represents the settings of a particular go command invocation.
+// It is a mode, plus a set of flag bits.
+type InvocationFlags int
+
+const (
+ // Normal is appropriate for commands that might be run by a user and don't
+ // deliberately modify go.mod files, e.g. `go test`.
+ Normal InvocationFlags = iota
+ // WriteTemporaryModFile is for commands that need information from a
+ // modified version of the user's go.mod file, e.g. `go mod tidy` used to
+ // generate diagnostics.
+ WriteTemporaryModFile
+ // LoadWorkspace is for packages.Load, and other operations that should
+ // consider the whole workspace at once.
+ LoadWorkspace
+
+ // AllowNetwork is a flag bit that indicates the invocation should be
+ // allowed to access the network.
+ AllowNetwork InvocationFlags = 1 << 10
+)
+
+func (m InvocationFlags) Mode() InvocationFlags {
+ return m & (AllowNetwork - 1)
+}
+
+func (m InvocationFlags) AllowNetwork() bool {
+ return m&AllowNetwork != 0
+}
+
+// View represents a single workspace.
+// This is the level at which we maintain configuration like working directory
+// and build tags.
+type View interface {
+ // Name returns the name this view was constructed with.
+ Name() string
+
+ // Folder returns the folder with which this view was created.
+ Folder() span.URI
+
+ // Options returns a copy of the Options for this view.
+ Options() *Options
+
+ // Snapshot returns the current snapshot for the view, and a
+ // release function that must be called when the Snapshot is
+ // no longer needed.
+ //
+ // If the view is shut down, the resulting error will be non-nil, and the
+ // release function need not be called.
+ Snapshot() (Snapshot, func(), error)
+
+ // IsGoPrivatePath reports whether target is a private import path, as identified
+ // by the GOPRIVATE environment variable.
+ IsGoPrivatePath(path string) bool
+
+ // ModuleUpgrades returns known module upgrades for the dependencies of
+ // modfile.
+ ModuleUpgrades(modfile span.URI) map[string]string
+
+ // RegisterModuleUpgrades registers that upgrades exist for the given modules
+ // required by modfile.
+ RegisterModuleUpgrades(modfile span.URI, upgrades map[string]string)
+
+ // ClearModuleUpgrades clears all upgrades for the modules in modfile.
+ ClearModuleUpgrades(modfile span.URI)
+
+ // Vulnerabilities returns known vulnerabilities for the given modfile.
+ // TODO(suzmue): replace command.Vuln with a different type, maybe
+ // https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck/govulnchecklib#Summary?
+ Vulnerabilities(modfile ...span.URI) map[span.URI]*govulncheck.Result
+
+ // SetVulnerabilities resets the list of vulnerabilities that exists for the given modules
+ // required by modfile.
+ SetVulnerabilities(modfile span.URI, vulncheckResult *govulncheck.Result)
+
+ // FileKind returns the type of a file.
+ //
+ // We can't reliably deduce the kind from the file name alone,
+ // as some editors can be told to interpret a buffer as
+ // language different from the file name heuristic, e.g. that
+ // an .html file actually contains Go "html/template" syntax,
+ // or even that a .go file contains Python.
+ FileKind(FileHandle) FileKind
+
+ // GoVersion returns the configured Go version for this view.
+ GoVersion() int
+
+ // GoVersionString returns the go version string configured for this view.
+ // Unlike [GoVersion], this encodes the minor version and commit hash information.
+ GoVersionString() string
+}
+
+// A FileSource maps uris to FileHandles.
+type FileSource interface {
+ // GetFile returns the FileHandle for a given URI.
+ GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
+}
+
+// A MetadataSource maps package IDs to metadata.
+//
+// TODO(rfindley): replace this with a concrete metadata graph, once it is
+// exposed from the snapshot.
+type MetadataSource interface {
+ // Metadata returns Metadata for the given package ID, or nil if it does not
+ // exist.
+ Metadata(PackageID) *Metadata
+}
+
+// A ParsedGoFile contains the results of parsing a Go file.
+type ParsedGoFile struct {
+ URI span.URI
+ Mode ParseMode
+ File *ast.File
+ Tok *token.File
+ // Source code used to build the AST. It may be different from the
+ // actual content of the file if we have fixed the AST.
+ Src []byte
+ Fixed bool
+ Mapper *protocol.Mapper // may map fixed Src, not file content
+ ParseErr scanner.ErrorList
+}
+
+// -- go/token domain convenience helpers --
+
+// PositionPos returns the token.Pos of protocol position p within the file.
+func (pgf *ParsedGoFile) PositionPos(p protocol.Position) (token.Pos, error) {
+ offset, err := pgf.Mapper.PositionOffset(p)
+ if err != nil {
+ return token.NoPos, err
+ }
+ return safetoken.Pos(pgf.Tok, offset)
+}
+
+// PosRange returns a protocol Range for the token.Pos interval in this file.
+func (pgf *ParsedGoFile) PosRange(start, end token.Pos) (protocol.Range, error) {
+ return pgf.Mapper.PosRange(pgf.Tok, start, end)
+}
+
+// PosMappedRange returns a MappedRange for the token.Pos interval in this file.
+// A MappedRange can be converted to any other form.
+func (pgf *ParsedGoFile) PosMappedRange(start, end token.Pos) (protocol.MappedRange, error) {
+ return pgf.Mapper.PosMappedRange(pgf.Tok, start, end)
+}
+
+// PosLocation returns a protocol Location for the token.Pos interval in this file.
+func (pgf *ParsedGoFile) PosLocation(start, end token.Pos) (protocol.Location, error) {
+ return pgf.Mapper.PosLocation(pgf.Tok, start, end)
+}
+
+// NodeRange returns a protocol Range for the ast.Node interval in this file.
+func (pgf *ParsedGoFile) NodeRange(node ast.Node) (protocol.Range, error) {
+ return pgf.Mapper.NodeRange(pgf.Tok, node)
+}
+
+// NodeMappedRange returns a MappedRange for the ast.Node interval in this file.
+// A MappedRange can be converted to any other form.
+func (pgf *ParsedGoFile) NodeMappedRange(node ast.Node) (protocol.MappedRange, error) {
+ return pgf.Mapper.NodeMappedRange(pgf.Tok, node)
+}
+
+// NodeLocation returns a protocol Location for the ast.Node interval in this file.
+func (pgf *ParsedGoFile) NodeLocation(node ast.Node) (protocol.Location, error) {
+ return pgf.Mapper.PosLocation(pgf.Tok, node.Pos(), node.End())
+}
+
+// RangePos parses a protocol Range back into the go/token domain.
+func (pgf *ParsedGoFile) RangePos(r protocol.Range) (token.Pos, token.Pos, error) {
+ start, end, err := pgf.Mapper.RangeOffsets(r)
+ if err != nil {
+ return token.NoPos, token.NoPos, err
+ }
+ return pgf.Tok.Pos(start), pgf.Tok.Pos(end), nil
+}
+
+// A ParsedModule contains the results of parsing a go.mod file.
+type ParsedModule struct {
+ URI span.URI
+ File *modfile.File
+ Mapper *protocol.Mapper
+ ParseErrors []*Diagnostic
+}
+
+// A ParsedWorkFile contains the results of parsing a go.work file.
+type ParsedWorkFile struct {
+ URI span.URI
+ File *modfile.WorkFile
+ Mapper *protocol.Mapper
+ ParseErrors []*Diagnostic
+}
+
+// A TidiedModule contains the results of running `go mod tidy` on a module.
+type TidiedModule struct {
+ // Diagnostics representing changes made by `go mod tidy`.
+ Diagnostics []*Diagnostic
+ // The bytes of the go.mod file after it was tidied.
+ TidiedContent []byte
+}
+
+// Metadata represents package metadata retrieved from go/packages.
+type Metadata struct {
+ ID PackageID
+ PkgPath PackagePath
+ Name PackageName
+ GoFiles []span.URI
+ CompiledGoFiles []span.URI
+ ForTest PackagePath // package path under test, or ""
+ TypesSizes types.Sizes
+ Errors []packages.Error
+ DepsByImpPath map[ImportPath]PackageID // may contain dups; empty ID => missing
+ DepsByPkgPath map[PackagePath]PackageID // values are unique and non-empty
+ Module *packages.Module
+ DepsErrors []*packagesinternal.PackageError
+ Diagnostics []*Diagnostic // processed diagnostics from 'go list'
+ LoadDir string // directory from which go/packages was run
+}
+
+func (m *Metadata) String() string { return string(m.ID) }
+
+// IsIntermediateTestVariant reports whether the given package is an
+// intermediate test variant, e.g. "net/http [net/url.test]".
+//
+// Such test variants arise when an x_test package (in this case net/url_test)
+// imports a package (in this case net/http) that itself imports the the
+// non-x_test package (in this case net/url).
+//
+// This is done so that the forward transitive closure of net/url_test has
+// only one package for the "net/url" import.
+// The intermediate test variant exists to hold the test variant import:
+//
+// net/url_test [net/url.test]
+//
+// | "net/http" -> net/http [net/url.test]
+// | "net/url" -> net/url [net/url.test]
+// | ...
+//
+// net/http [net/url.test]
+//
+// | "net/url" -> net/url [net/url.test]
+// | ...
+//
+// This restriction propagates throughout the import graph of net/http: for
+// every package imported by net/http that imports net/url, there must be an
+// intermediate test variant that instead imports "net/url [net/url.test]".
+//
+// As one can see from the example of net/url and net/http, intermediate test
+// variants can result in many additional packages that are essentially (but
+// not quite) identical. For this reason, we filter these variants wherever
+// possible.
+func (m *Metadata) IsIntermediateTestVariant() bool {
+ return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath
+}
+
+// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array.
+func RemoveIntermediateTestVariants(metas []*Metadata) []*Metadata {
+ res := metas[:0]
+ for _, m := range metas {
+ if !m.IsIntermediateTestVariant() {
+ res = append(res, m)
+ }
+ }
+ return res
+}
+
+var ErrViewExists = errors.New("view already exists for session")
+
+// FileModification represents a modification to a file.
+type FileModification struct {
+ URI span.URI
+ Action FileAction
+
+ // OnDisk is true if a watched file is changed on disk.
+ // If true, Version will be -1 and Text will be nil.
+ OnDisk bool
+
+ // Version will be -1 and Text will be nil when they are not supplied,
+ // specifically on textDocument/didClose and for on-disk changes.
+ Version int32
+ Text []byte
+
+ // LanguageID is only sent from the language client on textDocument/didOpen.
+ LanguageID string
+}
+
+type FileAction int
+
+const (
+ UnknownFileAction = FileAction(iota)
+ Open
+ Change
+ Close
+ Save
+ Create
+ Delete
+ InvalidateMetadata
+)
+
+func (a FileAction) String() string {
+ switch a {
+ case Open:
+ return "Open"
+ case Change:
+ return "Change"
+ case Close:
+ return "Close"
+ case Save:
+ return "Save"
+ case Create:
+ return "Create"
+ case Delete:
+ return "Delete"
+ case InvalidateMetadata:
+ return "InvalidateMetadata"
+ default:
+ return "Unknown"
+ }
+}
+
+var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version")
+var ErrNoModOnDisk = errors.New("go.mod file is not on disk")
+
+func IsNonFatalGoModError(err error) bool {
+ return err == ErrTmpModfileUnsupported || err == ErrNoModOnDisk
+}
+
+// ParseMode controls the content of the AST produced when parsing a source file.
+type ParseMode int
+
+const (
+ // ParseHeader specifies that the main package declaration and imports are needed.
+ // This is the mode used when attempting to examine the package graph structure.
+ ParseHeader ParseMode = iota
+
+ // ParseFull specifies the full AST is needed.
+ // This is used for files of direct interest where the entire contents must
+ // be considered.
+ ParseFull
+)
+
+// A FileHandle is an interface to files tracked by the LSP session, which may
+// be either files read from disk, or open in the editor session (overlays).
+type FileHandle interface {
+ // URI is the URI for this file handle.
+ // TODO(rfindley): this is not actually well-defined. In some cases, there
+ // may be more than one URI that resolve to the same FileHandle. Which one is
+ // this?
+ URI() span.URI
+ // FileIdentity returns a FileIdentity for the file, even if there was an
+ // error reading it.
+ FileIdentity() FileIdentity
+ // Saved reports whether the file has the same content on disk.
+ // For on-disk files, this is trivially true.
+ Saved() bool
+ // Version returns the file version, as defined by the LSP client.
+ // For on-disk file handles, Version returns 0.
+ Version() int32
+ // Read reads the contents of a file.
+ // If the file is not available, returns a nil slice and an error.
+ Read() ([]byte, error)
+}
+
+// A Hash is a cryptographic digest of the contents of a file.
+// (Although at 32B it is larger than a 16B string header, it is smaller
+// and has better locality than the string header + 64B of hex digits.)
+type Hash [sha256.Size]byte
+
+// HashOf returns the hash of some data.
+func HashOf(data []byte) Hash {
+ return Hash(sha256.Sum256(data))
+}
+
+// Hashf returns the hash of a printf-formatted string.
+func Hashf(format string, args ...interface{}) Hash {
+ // Although this looks alloc-heavy, it is faster than using
+ // Fprintf on sha256.New() because the allocations don't escape.
+ return HashOf([]byte(fmt.Sprintf(format, args...)))
+}
+
+// String returns the digest as a string of hex digits.
+func (h Hash) String() string {
+ return fmt.Sprintf("%64x", [sha256.Size]byte(h))
+}
+
+// Less returns true if the given hash is less than the other.
+func (h Hash) Less(other Hash) bool {
+ return bytes.Compare(h[:], other[:]) < 0
+}
+
+// XORWith updates *h to *h XOR h2.
+func (h *Hash) XORWith(h2 Hash) {
+ // Small enough that we don't need crypto/subtle.XORBytes.
+ for i := range h {
+ h[i] ^= h2[i]
+ }
+}
+
+// FileIdentity uniquely identifies a file at a version from a FileSystem.
+type FileIdentity struct {
+ URI span.URI
+ Hash Hash // digest of file contents
+}
+
+func (id FileIdentity) String() string {
+ return fmt.Sprintf("%s%s", id.URI, id.Hash)
+}
+
+// FileKind describes the kind of the file in question.
+// It can be one of Go,mod, Sum, or Tmpl.
+type FileKind int
+
+const (
+ // UnknownKind is a file type we don't know about.
+ UnknownKind = FileKind(iota)
+
+ // Go is a normal go source file.
+ Go
+ // Mod is a go.mod file.
+ Mod
+ // Sum is a go.sum file.
+ Sum
+ // Tmpl is a template file.
+ Tmpl
+ // Work is a go.work file.
+ Work
+)
+
+func (k FileKind) String() string {
+ switch k {
+ case Go:
+ return "go"
+ case Mod:
+ return "go.mod"
+ case Sum:
+ return "go.sum"
+ case Tmpl:
+ return "tmpl"
+ case Work:
+ return "go.work"
+ default:
+ return fmt.Sprintf("internal error: unknown file kind %d", k)
+ }
+}
+
+// Analyzer represents a go/analysis analyzer with some boolean properties
+// that let the user know how to use the analyzer.
+type Analyzer struct {
+ Analyzer *analysis.Analyzer
+
+ // Enabled reports whether the analyzer is enabled. This value can be
+ // configured per-analysis in user settings. For staticcheck analyzers,
+ // the value of the Staticcheck setting overrides this field.
+ //
+ // Most clients should use the IsEnabled method.
+ Enabled bool
+
+ // Fix is the name of the suggested fix name used to invoke the suggested
+ // fixes for the analyzer. It is non-empty if we expect this analyzer to
+ // provide its fix separately from its diagnostics. That is, we should apply
+ // the analyzer's suggested fixes through a Command, not a TextEdit.
+ Fix string
+
+ // ActionKind is the kind of code action this analyzer produces. If
+ // unspecified the type defaults to quickfix.
+ ActionKind []protocol.CodeActionKind
+
+ // Severity is the severity set for diagnostics reported by this
+ // analyzer. If left unset it defaults to Warning.
+ Severity protocol.DiagnosticSeverity
+}
+
+func (a *Analyzer) String() string { return a.Analyzer.String() }
+
+// IsEnabled reports whether this analyzer is enabled by the given options.
+func (a Analyzer) IsEnabled(options *Options) bool {
+ // Staticcheck analyzers can only be enabled when staticcheck is on.
+ if _, ok := options.StaticcheckAnalyzers[a.Analyzer.Name]; ok {
+ if !options.Staticcheck {
+ return false
+ }
+ }
+ if enabled, ok := options.Analyses[a.Analyzer.Name]; ok {
+ return enabled
+ }
+ return a.Enabled
+}
+
+// Declare explicit types for package paths, names, and IDs to ensure that we
+// never use an ID where a path belongs, and vice versa. If we confused these,
+// it would result in confusing errors because package IDs often look like
+// package paths.
+type (
+ PackageID string // go list's unique identifier for a package (e.g. "vendor/example.com/foo [vendor/example.com/bar.test]")
+ PackagePath string // name used to prefix linker symbols (e.g. "vendor/example.com/foo")
+ PackageName string // identifier in 'package' declaration (e.g. "foo")
+ ImportPath string // path that appears in an import declaration (e.g. "example.com/foo")
+)
+
+// Package represents a Go package that has been parsed and type-checked.
+//
+// By design, there is no way to reach from a Package to the Package
+// representing one of its dependencies.
+//
+// Callers must not assume that two Packages share the same
+// token.FileSet or types.Importer and thus have commensurable
+// token.Pos values or types.Objects. Instead, use stable naming
+// schemes, such as (URI, byte offset) for positions, or (PackagePath,
+// objectpath.Path) for exported declarations.
+type Package interface {
+ Metadata() *Metadata
+
+ // Results of parsing:
+ FileSet() *token.FileSet
+ ParseMode() ParseMode
+ CompiledGoFiles() []*ParsedGoFile // (borrowed)
+ File(uri span.URI) (*ParsedGoFile, error)
+ GetSyntax() []*ast.File // (borrowed)
+ HasParseErrors() bool
+
+ // Results of type checking:
+ GetTypes() *types.Package
+ GetTypesInfo() *types.Info
+ DependencyTypes(PackagePath) *types.Package // nil for indirect dependency of no consequence
+ HasTypeErrors() bool
+ DiagnosticsForFile(ctx context.Context, s Snapshot, uri span.URI) ([]*Diagnostic, error)
+}
+
+type unit = struct{}
+
+// A CriticalError is a workspace-wide error that generally prevents gopls from
+// functioning correctly. In the presence of critical errors, other diagnostics
+// in the workspace may not make sense.
+type CriticalError struct {
+ // MainError is the primary error. Must be non-nil.
+ MainError error
+
+ // Diagnostics contains any supplemental (structured) diagnostics.
+ Diagnostics []*Diagnostic
+}
+
+// An Diagnostic corresponds to an LSP Diagnostic.
+// https://microsoft.github.io/language-server-protocol/specification#diagnostic
+type Diagnostic struct {
+ URI span.URI
+ Range protocol.Range
+ Severity protocol.DiagnosticSeverity
+ Code string
+ CodeHref string
+
+ // Source is a human-readable description of the source of the error.
+ // Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name.
+ Source DiagnosticSource
+
+ Message string
+
+ Tags []protocol.DiagnosticTag
+ Related []protocol.DiagnosticRelatedInformation
+
+ // Fields below are used internally to generate quick fixes. They aren't
+ // part of the LSP spec and don't leave the server.
+ SuggestedFixes []SuggestedFix
+}
+
+func (d *Diagnostic) String() string {
+ return fmt.Sprintf("%v: %s", d.Range, d.Message)
+}
+
+type DiagnosticSource string
+
+const (
+ UnknownError DiagnosticSource = "<Unknown source>"
+ ListError DiagnosticSource = "go list"
+ ParseError DiagnosticSource = "syntax"
+ TypeError DiagnosticSource = "compiler"
+ ModTidyError DiagnosticSource = "go mod tidy"
+ OptimizationDetailsError DiagnosticSource = "optimizer details"
+ UpgradeNotification DiagnosticSource = "upgrade available"
+ Vulncheck DiagnosticSource = "vulncheck imports"
+ Govulncheck DiagnosticSource = "govulncheck"
+ TemplateError DiagnosticSource = "template"
+ WorkFileError DiagnosticSource = "go.work file"
+)
+
+func AnalyzerErrorKind(name string) DiagnosticSource {
+ return DiagnosticSource(name)
+}