Robert Findley has uploaded this change for review.
gopls/internal/lsp: invert the import between cache and source
After much pulling at this thread, we can finally invert the
relationship between the cache package and source package.
Change-Id: I34c9069231dd27a4fc0d88ae9d688d2a38e81d27
---
M gopls/internal/lsp/cache/check.go
M gopls/internal/lsp/cache/diagnostics.go
M gopls/internal/lsp/cache/errors.go
A gopls/internal/lsp/cache/filterer.go
M gopls/internal/lsp/cache/metadata/metadata.go
M gopls/internal/lsp/cache/mod.go
M gopls/internal/lsp/cache/mod_tidy.go
M gopls/internal/lsp/cache/mod_vuln.go
M gopls/internal/lsp/cache/pkg.go
M gopls/internal/lsp/cache/snapshot.go
M gopls/internal/lsp/cache/symbols.go
M gopls/internal/lsp/code_action.go
M gopls/internal/lsp/command.go
M gopls/internal/lsp/diagnostics.go
M gopls/internal/lsp/link.go
M gopls/internal/lsp/mod/code_lens.go
M gopls/internal/lsp/mod/diagnostics.go
M gopls/internal/lsp/mod/format.go
M gopls/internal/lsp/mod/hover.go
M gopls/internal/lsp/mod/inlayhint.go
M gopls/internal/lsp/semantic.go
M gopls/internal/lsp/server.go
M gopls/internal/lsp/source/add_import.go
M gopls/internal/lsp/source/call_hierarchy.go
M gopls/internal/lsp/source/change_signature.go
M gopls/internal/lsp/source/code_lens.go
M gopls/internal/lsp/source/completion/completion.go
M gopls/internal/lsp/source/completion/package.go
M gopls/internal/lsp/source/completion/statements.go
M gopls/internal/lsp/source/definition.go
M gopls/internal/lsp/source/diagnostics.go
M gopls/internal/lsp/source/fix.go
M gopls/internal/lsp/source/folding_range.go
M gopls/internal/lsp/source/format.go
M gopls/internal/lsp/source/gc_annotations.go
M gopls/internal/lsp/source/highlight.go
M gopls/internal/lsp/source/hover.go
M gopls/internal/lsp/source/implementation.go
M gopls/internal/lsp/source/inlay_hint.go
M gopls/internal/lsp/source/inline.go
M gopls/internal/lsp/source/inline_all.go
M gopls/internal/lsp/source/known_packages.go
M gopls/internal/lsp/source/linkname.go
M gopls/internal/lsp/source/references.go
M gopls/internal/lsp/source/rename.go
M gopls/internal/lsp/source/rename_check.go
M gopls/internal/lsp/source/signature_help.go
M gopls/internal/lsp/source/snapshot.go
M gopls/internal/lsp/source/stub.go
M gopls/internal/lsp/source/symbols.go
M gopls/internal/lsp/source/type_definition.go
M gopls/internal/lsp/source/types_format.go
M gopls/internal/lsp/source/util.go
M gopls/internal/lsp/source/workspace_symbol.go
M gopls/internal/lsp/source/workspace_symbol_test.go
M gopls/internal/lsp/template/highlight.go
M gopls/internal/lsp/template/implementations.go
M gopls/internal/lsp/template/symbols.go
M gopls/internal/lsp/work/completion.go
M gopls/internal/lsp/work/diagnostics.go
M gopls/internal/lsp/work/format.go
M gopls/internal/lsp/work/hover.go
M gopls/internal/lsp/workspace_symbol.go
M gopls/internal/regtest/diagnostics/golist_test.go
M gopls/internal/regtest/misc/vuln_test.go
M gopls/internal/vulncheck/scan/command.go
D gopls/internal/vulncheck/scan/util.go
67 files changed, 965 insertions(+), 1,011 deletions(-)
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index dea8df3..1ed7f47 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -30,7 +30,6 @@
"golang.org/x/tools/gopls/internal/lsp/filecache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/analysisinternal"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/tag"
@@ -97,8 +96,8 @@
// This is different from having type-checking errors: a failure to type-check
// indicates context cancellation or otherwise significant failure to perform
// the type-checking operation.
-func (s *Snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]Package_, error) {
- pkgs := make([]Package_, len(ids))
+func (s *Snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]*Package, error) {
+ pkgs := make([]*Package, len(ids))
var (
needIDs []PackageID // ids to type-check
@@ -204,7 +203,7 @@
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&meta)
+ metadata.RemoveIntermediateTestVariants(&meta)
for _, m := range meta {
openPackages[m.ID] = true
}
@@ -782,7 +781,7 @@
// only be concerned with parsing and type checking.
// (nevertheless, since the lifetime of load diagnostics matches that of the
// Metadata, it is convienient to memoize them here).
- loadDiagnostics []*source.Diagnostic
+ loadDiagnostics []*Diagnostic
// Local data:
@@ -1611,6 +1610,23 @@
return cfg
}
+// IsValidImport returns whether importPkgPath is importable
+// by pkgPath
+func IsValidImport(pkgPath, importPkgPath PackagePath) bool {
+ i := strings.LastIndex(string(importPkgPath), "/internal/")
+ if i == -1 {
+ return true
+ }
+ // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to
+ // operate on package IDs, not package paths.
+ if metadata.IsCommandLineArguments(PackageID(pkgPath)) {
+ return true
+ }
+ // TODO(rfindley): this is wrong. mod.testx/p should not be able to
+ // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q
+ return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
+}
+
// depsErrors creates diagnostics for each metadata error (e.g. import cycle).
// These may be attached to import declarations in the transitive source files
// of pkg, or to 'requires' declarations in the package's go.mod file.
@@ -1836,11 +1852,11 @@
msg += fmt.Sprintf(" (this error: %v)", e.Msg)
}
}
- diag := &source.Diagnostic{
+ diag := &Diagnostic{
URI: pgf.URI,
Range: rng,
Severity: protocol.SeverityError,
- Source: source.TypeError,
+ Source: TypeError,
Message: msg,
}
if code != 0 {
diff --git a/gopls/internal/lsp/cache/diagnostics.go b/gopls/internal/lsp/cache/diagnostics.go
index 1a66cde..818b62f 100644
--- a/gopls/internal/lsp/cache/diagnostics.go
+++ b/gopls/internal/lsp/cache/diagnostics.go
@@ -6,11 +6,86 @@
import (
"encoding/json"
+ "fmt"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/lsp/protocol"
)
+// 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 {
+ // TODO(adonovan): should be a protocol.URI, for symmetry.
+ URI protocol.DocumentURI // of diagnosed file (not diagnostic documentation)
+ 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 historically didn't leave the server.
+ //
+ // Update(2023-05): version 3.16 of the LSP spec included support for the
+ // Diagnostic.data field, which holds arbitrary data preserved in the
+ // diagnostic for codeAction requests. This field allows bundling additional
+ // information for quick-fixes, and gopls can (and should) use this
+ // information to avoid re-evaluating diagnostics in code-action handlers.
+ //
+ // In order to stage this transition incrementally, the 'BundledFixes' field
+ // may store a 'bundled' (=json-serialized) form of the associated
+ // SuggestedFixes. Not all diagnostics have their fixes bundled.
+ BundledFixes *json.RawMessage
+ 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"
+ ConsistencyInfo DiagnosticSource = "consistency"
+)
+
+type SuggestedFix struct {
+ Title string
+ Edits map[protocol.DocumentURI][]protocol.TextEdit
+ Command *protocol.Command
+ ActionKind protocol.CodeActionKind
+}
+
// SuggestedFixFromCommand returns a suggested fix to run the given command.
func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix {
return SuggestedFix{
diff --git a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors.go
index 27a883d..19117ee 100644
--- a/gopls/internal/lsp/cache/errors.go
+++ b/gopls/internal/lsp/cache/errors.go
@@ -23,6 +23,7 @@
"golang.org/x/tools/go/packages"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/analysis/embeddirective"
"golang.org/x/tools/gopls/internal/lsp/command"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
@@ -270,6 +271,39 @@
return srcDiags
}
+// canFixFuncs maps an analyer to a function that determines whether or not a
+// fix is possible for the given diagnostic.
+//
+// TODO(rfindley): clean this up.
+var canFixFuncs = map[settings.Fix]func(*Diagnostic) bool{
+ settings.AddEmbedImport: fixedByImportingEmbed,
+}
+
+// fixedByImportingEmbed returns true if diag can be fixed by addEmbedImport.
+func fixedByImportingEmbed(diag *Diagnostic) bool {
+ if diag == nil {
+ return false
+ }
+ return diag.Message == embeddirective.MissingImportMessage
+}
+
+// canFix returns true if Analyzer.Fix can fix the Diagnostic.
+//
+// It returns true by default: only if the analyzer is configured explicitly to
+// ignore this diagnostic does it return false.
+//
+// TODO(rfindley): reconcile the semantics of 'Fix' and
+// 'suggestedAnalysisFixes'.
+func canFix(a *settings.Analyzer, d *Diagnostic) bool {
+ f, ok := canFixFuncs[a.Fix]
+ if !ok {
+ // See the above TODO: this doesn't make sense, but preserves pre-existing
+ // semantics.
+ return true
+ }
+ return f(d)
+}
+
// toSourceDiagnostic converts a gobDiagnostic to "source" form.
func toSourceDiagnostic(srcAnalyzer *settings.Analyzer, gobDiag *gobDiagnostic) *Diagnostic {
var related []protocol.DiagnosticRelatedInformation
@@ -298,7 +332,7 @@
Related: related,
Tags: srcAnalyzer.Tag,
}
- if CanFix(srcAnalyzer, diag) {
+ if canFix(srcAnalyzer, diag) {
fixes := suggestedAnalysisFixes(gobDiag, kinds)
if srcAnalyzer.Fix != "" {
cmd, err := command.NewApplyFixCommand(gobDiag.Message, command.ApplyFixArgs{
@@ -349,6 +383,15 @@
return BuildLink(linkTarget, "golang.org/x/tools/internal/typesinternal", code.String())
}
+// BuildLink constructs a URL with the given target, path, and anchor.
+func BuildLink(target, path, anchor string) string {
+ link := fmt.Sprintf("https://%s/%s", target, path)
+ if anchor == "" {
+ return link
+ }
+ return link + "#" + anchor
+}
+
func suggestedAnalysisFixes(diag *gobDiagnostic, kinds []protocol.CodeActionKind) []SuggestedFix {
var fixes []SuggestedFix
for _, fix := range diag.SuggestedFixes {
diff --git a/gopls/internal/lsp/cache/filterer.go b/gopls/internal/lsp/cache/filterer.go
new file mode 100644
index 0000000..0ec1836
--- /dev/null
+++ b/gopls/internal/lsp/cache/filterer.go
@@ -0,0 +1,83 @@
+// Copyright 2023 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 (
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+type Filterer struct {
+ // Whether a filter is excluded depends on the operator (first char of the raw filter).
+ // Slices filters and excluded then should have the same length.
+ filters []*regexp.Regexp
+ excluded []bool
+}
+
+// NewFilterer computes regular expression form of all raw filters
+func NewFilterer(rawFilters []string) *Filterer {
+ var f Filterer
+ for _, filter := range rawFilters {
+ filter = path.Clean(filepath.ToSlash(filter))
+ // TODO(dungtuanle): fix: validate [+-] prefix.
+ op, prefix := filter[0], filter[1:]
+ // convertFilterToRegexp adds "/" at the end of prefix to handle cases where a filter is a prefix of another filter.
+ // For example, it prevents [+foobar, -foo] from excluding "foobar".
+ f.filters = append(f.filters, convertFilterToRegexp(filepath.ToSlash(prefix)))
+ f.excluded = append(f.excluded, op == '-')
+ }
+
+ return &f
+}
+
+// Disallow return true if the path is excluded from the filterer's filters.
+func (f *Filterer) Disallow(path string) bool {
+ // Ensure trailing but not leading slash.
+ path = strings.TrimPrefix(path, "/")
+ if !strings.HasSuffix(path, "/") {
+ path += "/"
+ }
+
+ // TODO(adonovan): opt: iterate in reverse and break at first match.
+ excluded := false
+ for i, filter := range f.filters {
+ if filter.MatchString(path) {
+ excluded = f.excluded[i] // last match wins
+ }
+ }
+ return excluded
+}
+
+// convertFilterToRegexp replaces glob-like operator substrings in a string file path to their equivalent regex forms.
+// Supporting glob-like operators:
+// - **: match zero or more complete path segments
+func convertFilterToRegexp(filter string) *regexp.Regexp {
+ if filter == "" {
+ return regexp.MustCompile(".*")
+ }
+ var ret strings.Builder
+ ret.WriteString("^")
+ segs := strings.Split(filter, "/")
+ for _, seg := range segs {
+ // Inv: seg != "" since path is clean.
+ if seg == "**" {
+ ret.WriteString(".*")
+ } else {
+ ret.WriteString(regexp.QuoteMeta(seg))
+ }
+ ret.WriteString("/")
+ }
+ pattern := ret.String()
+
+ // Remove unnecessary "^.*" prefix, which increased
+ // BenchmarkWorkspaceSymbols time by ~20% (even though
+ // filter CPU time increased by only by ~2.5%) when the
+ // default filter was changed to "**/node_modules".
+ pattern = strings.TrimPrefix(pattern, "^.*")
+
+ return regexp.MustCompile(pattern)
+}
diff --git a/gopls/internal/lsp/cache/metadata/metadata.go b/gopls/internal/lsp/cache/metadata/metadata.go
index 642b3a2..4a624fe 100644
--- a/gopls/internal/lsp/cache/metadata/metadata.go
+++ b/gopls/internal/lsp/cache/metadata/metadata.go
@@ -207,3 +207,16 @@
return postorder[ids[i]] < postorder[ids[j]]
})
}
+
+// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array.
+// We use a pointer to a slice make it impossible to forget to use the result.
+func RemoveIntermediateTestVariants(pmetas *[]*Metadata) {
+ metas := *pmetas
+ res := metas[:0]
+ for _, m := range metas {
+ if !m.IsIntermediateTestVariant() {
+ res = append(res, m)
+ }
+ }
+ *pmetas = res
+}
diff --git a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go
index 10bb1ff..59c218f 100644
--- a/gopls/internal/lsp/cache/mod.go
+++ b/gopls/internal/lsp/cache/mod.go
@@ -23,6 +23,14 @@
"golang.org/x/tools/internal/memoize"
)
+// A ParsedModule contains the results of parsing a go.mod file.
+type ParsedModule struct {
+ URI protocol.DocumentURI
+ File *modfile.File
+ Mapper *protocol.Mapper
+ ParseErrors []*Diagnostic
+}
+
// ParseMod parses a go.mod file, using a cache. It may return partial results and an error.
func (s *Snapshot) ParseMod(ctx context.Context, fh file.Handle) (*ParsedModule, error) {
uri := fh.URI()
@@ -100,6 +108,14 @@
}, parseErr
}
+// A ParsedWorkFile contains the results of parsing a go.work file.
+type ParsedWorkFile struct {
+ URI protocol.DocumentURI
+ File *modfile.WorkFile
+ Mapper *protocol.Mapper
+ ParseErrors []*Diagnostic
+}
+
// ParseWork parses a go.work file, using a cache. It may return partial results and an error.
// TODO(adonovan): move to new work.go file.
func (s *Snapshot) ParseWork(ctx context.Context, fh file.Handle) (*ParsedWorkFile, error) {
diff --git a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_tidy.go
index 31a35a5..56697bb 100644
--- a/gopls/internal/lsp/cache/mod_tidy.go
+++ b/gopls/internal/lsp/cache/mod_tidy.go
@@ -29,6 +29,14 @@
// This error is sought by mod diagnostics.
var ErrNoModOnDisk = errors.New("go.mod file is not on disk")
+// 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
+}
+
// ModTidy returns the go.mod file that would be obtained by running
// "go mod tidy". Concurrent requests are combined into a single command.
func (s *Snapshot) ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error) {
diff --git a/gopls/internal/lsp/cache/mod_vuln.go b/gopls/internal/lsp/cache/mod_vuln.go
index 0f70333..b2f55fe 100644
--- a/gopls/internal/lsp/cache/mod_vuln.go
+++ b/gopls/internal/lsp/cache/mod_vuln.go
@@ -6,11 +6,24 @@
import (
"context"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+ "strings"
+ "sync"
+ "golang.org/x/mod/semver"
+ "golang.org/x/sync/errgroup"
+ "golang.org/x/tools/go/packages"
+ "golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/vulncheck"
- "golang.org/x/tools/gopls/internal/vulncheck/scan"
+ "golang.org/x/tools/gopls/internal/vulncheck/govulncheck"
+ "golang.org/x/tools/gopls/internal/vulncheck/osv"
+ isem "golang.org/x/tools/gopls/internal/vulncheck/semver"
"golang.org/x/tools/internal/memoize"
+ "golang.org/x/vuln/scan"
)
// ModVuln returns import vulnerability analysis for the given go.mod URI.
@@ -28,7 +41,7 @@
// Cache miss?
if !hit {
handle := memoize.NewPromise("modVuln", func(ctx context.Context, arg interface{}) interface{} {
- result, err := scan.VulnerablePackages(ctx, arg.(*Snapshot))
+ result, err := vulnerablePackages(ctx, arg.(*Snapshot))
return modVuln{result, err}
})
@@ -46,3 +59,331 @@
res := v.(modVuln)
return res.result, res.err
}
+
+// GoVersionForVulnTest is an internal environment variable used in gopls
+// testing to examine govulncheck behavior with a go version different
+// than what `go version` returns in the system.
+const GoVersionForVulnTest = "_GOPLS_TEST_VULNCHECK_GOVERSION"
+
+// vulnerablePackages queries the vulndb and reports which vulnerabilities
+// apply to this snapshot. The result contains a set of packages,
+// grouped by vuln ID and by module. This implements the "import-based"
+// vulnerability report on go.mod files.
+func vulnerablePackages(ctx context.Context, snapshot *Snapshot) (*vulncheck.Result, error) {
+ // TODO(hyangah): can we let 'govulncheck' take a package list
+ // used in the workspace and implement this function?
+
+ // We want to report the intersection of vulnerable packages in the vulndb
+ // and packages transitively imported by this module ('go list -deps all').
+ // We use snapshot.AllMetadata to retrieve the list of packages
+ // as an approximation.
+ //
+ // TODO(hyangah): snapshot.AllMetadata is a superset of
+ // `go list all` - e.g. when the workspace has multiple main modules
+ // (multiple go.mod files), that can include packages that are not
+ // used by this module. Vulncheck behavior with go.work is not well
+ // defined. Figure out the meaning, and if we decide to present
+ // the result as if each module is analyzed independently, make
+ // gopls track a separate build list for each module and use that
+ // information instead of snapshot.AllMetadata.
+ allMeta, err := snapshot.AllMetadata(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO(hyangah): handle vulnerabilities in the standard library.
+
+ // Group packages by modules since vuln db is keyed by module.
+ metadataByModule := map[metadata.PackagePath][]*metadata.Metadata{}
+ for _, md := range allMeta {
+ modulePath := metadata.PackagePath(osv.GoStdModulePath)
+ if mi := md.Module; mi != nil {
+ modulePath = metadata.PackagePath(mi.Path)
+ }
+ metadataByModule[modulePath] = append(metadataByModule[modulePath], md)
+ }
+
+ var (
+ mu sync.Mutex
+ // Keys are osv.Entry.ID
+ osvs = map[string]*osv.Entry{}
+ findings []*govulncheck.Finding
+ )
+
+ goVersion := snapshot.Options().Env[GoVersionForVulnTest]
+ if goVersion == "" {
+ goVersion = snapshot.GoVersionString()
+ }
+
+ stdlibModule := &packages.Module{
+ Path: osv.GoStdModulePath,
+ Version: goVersion,
+ }
+
+ // GOVULNDB may point the test db URI.
+ db := GetEnv(snapshot, "GOVULNDB")
+
+ var group errgroup.Group
+ group.SetLimit(10) // limit govulncheck api runs
+ for _, mds := range metadataByModule {
+ mds := mds
+ group.Go(func() error {
+ effectiveModule := stdlibModule
+ if m := mds[0].Module; m != nil {
+ effectiveModule = m
+ }
+ for effectiveModule.Replace != nil {
+ effectiveModule = effectiveModule.Replace
+ }
+ ver := effectiveModule.Version
+ if ver == "" || !isem.Valid(ver) {
+ // skip invalid version strings. the underlying scan api is strict.
+ return nil
+ }
+
+ // TODO(hyangah): batch these requests and add in-memory cache for efficiency.
+ vulns, err := osvsByModule(ctx, db, effectiveModule.Path+"@"+ver)
+ if err != nil {
+ return err
+ }
+ if len(vulns) == 0 { // No known vulnerability.
+ return nil
+ }
+
+ // set of packages in this module known to gopls.
+ // This will be lazily initialized when we need it.
+ var knownPkgs map[metadata.PackagePath]bool
+
+ // Report vulnerabilities that affect packages of this module.
+ for _, entry := range vulns {
+ var vulnerablePkgs []*govulncheck.Finding
+ fixed := fixedVersion(effectiveModule.Path, entry.Affected)
+
+ for _, a := range entry.Affected {
+ if a.Module.Ecosystem != osv.GoEcosystem || a.Module.Path != effectiveModule.Path {
+ continue
+ }
+ for _, imp := range a.EcosystemSpecific.Packages {
+ if knownPkgs == nil {
+ knownPkgs = toPackagePathSet(mds)
+ }
+ if knownPkgs[metadata.PackagePath(imp.Path)] {
+ vulnerablePkgs = append(vulnerablePkgs, &govulncheck.Finding{
+ OSV: entry.ID,
+ FixedVersion: fixed,
+ Trace: []*govulncheck.Frame{
+ {
+ Module: effectiveModule.Path,
+ Version: effectiveModule.Version,
+ Package: imp.Path,
+ },
+ },
+ })
+ }
+ }
+ }
+ if len(vulnerablePkgs) == 0 {
+ continue
+ }
+ mu.Lock()
+ osvs[entry.ID] = entry
+ findings = append(findings, vulnerablePkgs...)
+ mu.Unlock()
+ }
+ return nil
+ })
+ }
+ if err := group.Wait(); err != nil {
+ return nil, err
+ }
+
+ // Sort so the results are deterministic.
+ sort.Slice(findings, func(i, j int) bool {
+ x, y := findings[i], findings[j]
+ if x.OSV != y.OSV {
+ return x.OSV < y.OSV
+ }
+ return x.Trace[0].Package < y.Trace[0].Package
+ })
+ ret := &vulncheck.Result{
+ Entries: osvs,
+ Findings: findings,
+ Mode: vulncheck.ModeImports,
+ }
+ return ret, nil
+}
+
+// TODO(rfindley): this function was exposed during refactoring. Reconsider it.
+func GetEnv(snapshot *Snapshot, key string) string {
+ val, ok := snapshot.Options().Env[key]
+ if ok {
+ return val
+ }
+ return os.Getenv(key)
+}
+
+// toPackagePathSet transforms the metadata to a set of package paths.
+func toPackagePathSet(mds []*metadata.Metadata) map[metadata.PackagePath]bool {
+ pkgPaths := make(map[metadata.PackagePath]bool, len(mds))
+ for _, md := range mds {
+ pkgPaths[md.PkgPath] = true
+ }
+ return pkgPaths
+}
+
+func fixedVersion(modulePath string, affected []osv.Affected) string {
+ fixed := latestFixed(modulePath, affected)
+ if fixed != "" {
+ fixed = versionString(modulePath, fixed)
+ }
+ return fixed
+}
+
+// latestFixed returns the latest fixed version in the list of affected ranges,
+// or the empty string if there are no fixed versions.
+func latestFixed(modulePath string, as []osv.Affected) string {
+ v := ""
+ for _, a := range as {
+ if a.Module.Path != modulePath {
+ continue
+ }
+ for _, r := range a.Ranges {
+ if r.Type == osv.RangeTypeSemver {
+ for _, e := range r.Events {
+ if e.Fixed != "" && (v == "" ||
+ semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) {
+ v = e.Fixed
+ }
+ }
+ }
+ }
+ }
+ return v
+}
+
+// versionString prepends a version string prefix (`v` or `go`
+// depending on the modulePath) to the given semver-style version string.
+func versionString(modulePath, version string) string {
+ if version == "" {
+ return ""
+ }
+ v := "v" + version
+ // These are internal Go module paths used by the vuln DB
+ // when listing vulns in standard library and the go command.
+ if modulePath == "stdlib" || modulePath == "toolchain" {
+ return semverToGoTag(v)
+ }
+ return v
+}
+
+// semverToGoTag returns the Go standard library repository tag corresponding
+// to semver, a version string without the initial "v".
+// Go tags differ from standard semantic versions in a few ways,
+// such as beginning with "go" instead of "v".
+func semverToGoTag(v string) string {
+ if strings.HasPrefix(v, "v0.0.0") {
+ return "master"
+ }
+ // Special case: v1.0.0 => go1.
+ if v == "v1.0.0" {
+ return "go1"
+ }
+ if !semver.IsValid(v) {
+ return fmt.Sprintf("<!%s:invalid semver>", v)
+ }
+ goVersion := semver.Canonical(v)
+ prerelease := semver.Prerelease(goVersion)
+ versionWithoutPrerelease := strings.TrimSuffix(goVersion, prerelease)
+ patch := strings.TrimPrefix(versionWithoutPrerelease, semver.MajorMinor(goVersion)+".")
+ if patch == "0" {
+ versionWithoutPrerelease = strings.TrimSuffix(versionWithoutPrerelease, ".0")
+ }
+ goVersion = fmt.Sprintf("go%s", strings.TrimPrefix(versionWithoutPrerelease, "v"))
+ if prerelease != "" {
+ // Go prereleases look like "beta1" instead of "beta.1".
+ // "beta1" is bad for sorting (since beta10 comes before beta9), so
+ // require the dot form.
+ i := finalDigitsIndex(prerelease)
+ if i >= 1 {
+ if prerelease[i-1] != '.' {
+ return fmt.Sprintf("<!%s:final digits in a prerelease must follow a period>", v)
+ }
+ // Remove the dot.
+ prerelease = prerelease[:i-1] + prerelease[i:]
+ }
+ goVersion += strings.TrimPrefix(prerelease, "-")
+ }
+ return goVersion
+}
+
+// finalDigitsIndex returns the index of the first digit in the sequence of digits ending s.
+// If s doesn't end in digits, it returns -1.
+func finalDigitsIndex(s string) int {
+ // Assume ASCII (since the semver package does anyway).
+ var i int
+ for i = len(s) - 1; i >= 0; i-- {
+ if s[i] < '0' || s[i] > '9' {
+ break
+ }
+ }
+ if i == len(s)-1 {
+ return -1
+ }
+ return i + 1
+}
+
+// osvsByModule runs a govulncheck database query.
+func osvsByModule(ctx context.Context, db, moduleVersion string) ([]*osv.Entry, error) {
+ var args []string
+ args = append(args, "-mode=query", "-json")
+ if db != "" {
+ args = append(args, "-db="+db)
+ }
+ args = append(args, moduleVersion)
+
+ ir, iw := io.Pipe()
+ handler := &osvReader{}
+
+ var g errgroup.Group
+ g.Go(func() error {
+ defer iw.Close() // scan API doesn't close cmd.Stderr/cmd.Stdout.
+ cmd := scan.Command(ctx, args...)
+ cmd.Stdout = iw
+ // TODO(hakim): Do we need to set cmd.Env = getEnvSlices(),
+ // or is the process environment good enough?
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ return cmd.Wait()
+ })
+ g.Go(func() error {
+ return govulncheck.HandleJSON(ir, handler)
+ })
+
+ if err := g.Wait(); err != nil {
+ return nil, err
+ }
+ return handler.entry, nil
+}
+
+// osvReader implements govulncheck.Handler.
+type osvReader struct {
+ entry []*osv.Entry
+}
+
+func (h *osvReader) OSV(entry *osv.Entry) error {
+ h.entry = append(h.entry, entry)
+ return nil
+}
+
+func (h *osvReader) Config(config *govulncheck.Config) error {
+ return nil
+}
+
+func (h *osvReader) Finding(finding *govulncheck.Finding) error {
+ return nil
+}
+
+func (h *osvReader) Progress(progress *govulncheck.Progress) error {
+ return nil
+}
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index ce1bdd2..2f58b1f 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -18,7 +18,6 @@
"golang.org/x/tools/gopls/internal/lsp/cache/parsego"
"golang.org/x/tools/gopls/internal/lsp/cache/xrefs"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
)
// Temporary refactoring, reversing the source import:
@@ -31,24 +30,8 @@
ImportPath = metadata.ImportPath
Metadata = metadata.Metadata
- // Diagnostics.
- Diagnostic = source.Diagnostic
- SuggestedFix = source.SuggestedFix
- DiagnosticSource = source.DiagnosticSource
-
// Computed objects.
- TidiedModule = source.TidiedModule
- ParsedGoFile = parsego.File
- ParsedModule = source.ParsedModule
- ParsedWorkFile = source.ParsedWorkFile
- Package_ = source.Package // renamed to avoid conflict
- Symbol = source.Symbol
-
- XrefIndex_ = source.XrefIndex // renamed to avoid conflict
- GlobalSnapshotID = source.GlobalSnapshotID
- InvocationFlags = source.InvocationFlags
- CriticalError = source.CriticalError
- Filterer = source.Filterer
+ ParsedGoFile = parsego.File
)
// Values
@@ -56,27 +39,6 @@
// Parse Modes
ParseHeader = parsego.ParseHeader
ParseFull = parsego.ParseFull
-
- // Diagnostic sources.
- ModTidyError = source.ModTidyError
- ListError = source.ListError
- ParseError = source.ParseError
- TypeError = source.TypeError
-
- // Invocation flags.
- Normal = source.Normal
- AllowNetwork = source.AllowNetwork
- LoadWorkspace = source.LoadWorkspace
- WriteTemporaryModFile = source.WriteTemporaryModFile
-)
-
-// Functions
-var (
- IsValidImport = source.IsValidImport
- RemoveIntermediateTestVariants = source.RemoveIntermediateTestVariants
- NewFilterer = source.NewFilterer
- BuildLink = source.BuildLink
- CanFix = source.CanFix
)
// A Package is the union of package metadata and type checking results.
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 99f3d0a..c607140 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -54,6 +54,13 @@
"golang.org/x/tools/internal/typesinternal"
)
+// 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
+
type Snapshot struct {
sequenceID uint64
globalID GlobalSnapshotID
@@ -425,6 +432,34 @@
return cfg
}
+// 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
+}
+
func (s *Snapshot) RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) {
_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
if err != nil {
@@ -668,11 +703,11 @@
return perFile, s.forEachPackage(ctx, ids, pre, post)
}
-func (s *Snapshot) References(ctx context.Context, ids ...PackageID) ([]XrefIndex_, error) {
+func (s *Snapshot) References(ctx context.Context, ids ...PackageID) ([]XrefIndex, error) {
ctx, done := event.Start(ctx, "cache.snapshot.References")
defer done()
- indexes := make([]XrefIndex_, len(ids))
+ indexes := make([]XrefIndex, len(ids))
pre := func(i int, ph *packageHandle) bool {
data, err := filecache.Get(xrefsKind, ph.key)
if err == nil { // hit
diff --git a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbols.go
index d4bc929..6abbd14 100644
--- a/gopls/internal/lsp/cache/symbols.go
+++ b/gopls/internal/lsp/cache/symbols.go
@@ -16,6 +16,15 @@
"golang.org/x/tools/gopls/internal/lsp/protocol"
)
+// Symbol holds a precomputed symbol value. Note: we avoid using the
+// protocol.SymbolInformation struct here in order to reduce the size of each
+// symbol.
+type Symbol struct {
+ Name string
+ Kind protocol.SymbolKind
+ Range protocol.Range
+}
+
// symbolize returns the result of symbolizing the file identified by uri, using a cache.
func (s *Snapshot) symbolize(ctx context.Context, uri protocol.DocumentURI) ([]Symbol, error) {
diff --git a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.go
index 488cbc3..0536ba7 100644
--- a/gopls/internal/lsp/code_action.go
+++ b/gopls/internal/lsp/code_action.go
@@ -97,7 +97,7 @@
searchFixes:
for _, fix := range fixes {
for _, diag := range fix.Diagnostics {
- if diag.Source == string(source.Govulncheck) || diag.Source == string(source.Vulncheck) {
+ if diag.Source == string(cache.Govulncheck) || diag.Source == string(cache.Vulncheck) {
vulnFixes[diag.Range] = append(vulnFixes[diag.Range], fix)
continue searchFixes
}
@@ -281,11 +281,11 @@
}
}
-func (s *server) findMatchingDiagnostics(uri protocol.DocumentURI, pd protocol.Diagnostic) []*source.Diagnostic {
+func (s *server) findMatchingDiagnostics(uri protocol.DocumentURI, pd protocol.Diagnostic) []*cache.Diagnostic {
s.diagnosticsMu.Lock()
defer s.diagnosticsMu.Unlock()
- var sds []*source.Diagnostic
+ var sds []*cache.Diagnostic
for _, report := range s.diagnostics[uri].reports {
for _, sd := range report.diags {
sameDiagnostic := (pd.Message == strings.TrimSpace(sd.Message) && // extra space may have been trimmed when converting to protocol.Diagnostic
@@ -367,7 +367,7 @@
return results
}
-func refactorExtract(ctx context.Context, snapshot source.Snapshot, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) {
+func refactorExtract(ctx context.Context, snapshot *cache.Snapshot, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) {
if rng.Start == rng.End {
return nil, nil
}
@@ -422,7 +422,7 @@
return actions, nil
}
-func refactorRewrite(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range) (_ []protocol.CodeAction, rerr error) {
+func refactorRewrite(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range) (_ []protocol.CodeAction, rerr error) {
// golang/go#61693: code actions were refactored to run outside of the
// analysis framework, but as a result they lost their panic recovery.
//
@@ -542,7 +542,7 @@
// This is true if:
// - [start, end) is contained within an unused field or parameter name
// - ... of a non-method function declaration.
-func canRemoveParameter(pkg source.Package, pgf *source.ParsedGoFile, rng protocol.Range) bool {
+func canRemoveParameter(pkg *cache.Package, pgf *source.ParsedGoFile, rng protocol.Range) bool {
info := source.FindParam(pgf, rng)
if info.Decl == nil || info.Field == nil {
return false
@@ -578,7 +578,7 @@
}
// refactorInline returns inline actions available at the specified range.
-func refactorInline(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range) ([]protocol.CodeAction, error) {
+func refactorInline(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.ParsedGoFile, fh file.Handle, rng protocol.Range) ([]protocol.CodeAction, error) {
var commands []protocol.Command
// If range is within call expression, offer inline action.
@@ -627,7 +627,7 @@
// bundled protocol.Diagnostic.Data field, and failing that by falling back on
// fetching a matching source.Diagnostic from the set of stored diagnostics for
// this file.
-func (s *server) codeActionsMatchingDiagnostics(ctx context.Context, uri protocol.DocumentURI, snapshot source.Snapshot, pds []protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) {
+func (s *server) codeActionsMatchingDiagnostics(ctx context.Context, uri protocol.DocumentURI, snapshot *cache.Snapshot, pds []protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) {
var actions []protocol.CodeAction
var unbundled []protocol.Diagnostic // diagnostics without bundled code actions in their Data field
for _, pd := range pds {
@@ -656,7 +656,7 @@
return actions, nil
}
-func codeActionsForDiagnostic(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) {
+func codeActionsForDiagnostic(ctx context.Context, snapshot *cache.Snapshot, sd *cache.Diagnostic, pd *protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) {
var actions []protocol.CodeAction
for _, fix := range sd.SuggestedFixes {
if !want[fix.ActionKind] {
@@ -684,7 +684,7 @@
return actions, nil
}
-func goTest(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) {
+func goTest(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) {
fns, err := source.TestsAndBenchmarks(pkg, pgf)
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go
index 08b1983..fb89fd9 100644
--- a/gopls/internal/lsp/command.go
+++ b/gopls/internal/lsp/command.go
@@ -372,7 +372,7 @@
// If golang/go#44119 is resolved, go mod vendor will instead modify
// modules.txt in-place. In that case we could theoretically allow this
// command to run concurrently.
- err := deps.snapshot.RunGoCommandPiped(ctx, source.Normal|source.AllowNetwork, &gocommand.Invocation{
+ err := deps.snapshot.RunGoCommandPiped(ctx, cache.Normal|cache.AllowNetwork, &gocommand.Invocation{
Verb: "mod",
Args: []string{"vendor"},
WorkingDir: filepath.Dir(args.URI.Path()),
@@ -456,7 +456,7 @@
// dropDependency returns the edits to remove the given require from the go.mod
// file.
-func dropDependency(snapshot source.Snapshot, pm *source.ParsedModule, modulePath string) ([]protocol.TextEdit, error) {
+func dropDependency(snapshot *cache.Snapshot, pm *cache.ParsedModule, modulePath string) ([]protocol.TextEdit, error) {
// We need a private copy of the parsed go.mod file, since we're going to
// modify it.
copied, err := modfile.Parse("", pm.Mapper.Content, nil)
@@ -516,7 +516,7 @@
Args: []string{pkgPath, "-v", "-count=1", "-run", fmt.Sprintf("^%s$", funcName)},
WorkingDir: filepath.Dir(uri.Path()),
}
- if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
+ if err := snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, out, out); err != nil {
if errors.Is(err, context.Canceled) {
return err
}
@@ -532,7 +532,7 @@
Args: []string{pkgPath, "-v", "-run=^$", "-bench", fmt.Sprintf("^%s$", funcName)},
WorkingDir: filepath.Dir(uri.Path()),
}
- if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
+ if err := snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, out, out); err != nil {
if errors.Is(err, context.Canceled) {
return err
}
@@ -595,7 +595,7 @@
WorkingDir: args.Dir.Path(),
}
stderr := io.MultiWriter(er, progress.NewWorkDoneWriter(ctx, deps.work))
- if err := deps.snapshot.RunGoCommandPiped(ctx, source.Normal, inv, er, stderr); err != nil {
+ if err := deps.snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, er, stderr); err != nil {
return err
}
return nil
@@ -608,7 +608,7 @@
progress: "Running go get",
}, func(ctx context.Context, deps commandDeps) error {
// Run on a throwaway go.mod, otherwise it'll write to the real one.
- stdout, err := deps.snapshot.RunGoCommandDirect(ctx, source.WriteTemporaryModFile|source.AllowNetwork, &gocommand.Invocation{
+ stdout, err := deps.snapshot.RunGoCommandDirect(ctx, cache.WriteTemporaryModFile|cache.AllowNetwork, &gocommand.Invocation{
Verb: "list",
Args: []string{"-f", "{{.Module.Path}}@{{.Module.Version}}", args.Pkg},
WorkingDir: filepath.Dir(args.URI.Path()),
@@ -739,8 +739,8 @@
}
// TODO(rfindley): inline.
-func (s *server) getUpgrades(ctx context.Context, snapshot source.Snapshot, uri protocol.DocumentURI, modules []string) (map[string]string, error) {
- stdout, err := snapshot.RunGoCommandDirect(ctx, source.Normal|source.AllowNetwork, &gocommand.Invocation{
+func (s *server) getUpgrades(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, modules []string) (map[string]string, error) {
+ stdout, err := snapshot.RunGoCommandDirect(ctx, cache.Normal|cache.AllowNetwork, &gocommand.Invocation{
Verb: "list",
Args: append([]string{"-m", "-u", "-json"}, modules...),
WorkingDir: filepath.Dir(uri.Path()),
diff --git a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.go
index f17eca1..499af8c 100644
--- a/gopls/internal/lsp/diagnostics.go
+++ b/gopls/internal/lsp/diagnostics.go
@@ -54,9 +54,9 @@
// A diagnosticReport holds results for a single diagnostic source.
type diagnosticReport struct {
- snapshotID source.GlobalSnapshotID // global snapshot ID on which the report was computed
- publishedHash string // last published hash for this (URI, source)
- diags map[string]*source.Diagnostic
+ snapshotID cache.GlobalSnapshotID // global snapshot ID on which the report was computed
+ publishedHash string // last published hash for this (URI, source)
+ diags map[string]*cache.Diagnostic
}
// fileReports holds a collection of diagnostic reports for a single file, as
@@ -79,7 +79,7 @@
// yet published.
//
// This prevents gopls from publishing stale diagnostics.
- publishedSnapshotID source.GlobalSnapshotID
+ publishedSnapshotID cache.GlobalSnapshotID
// publishedHash is a hash of the latest diagnostics published for the file.
publishedHash string
@@ -120,7 +120,7 @@
// hashDiagnostics computes a hash to identify diags.
//
// hashDiagnostics mutates its argument (via sorting).
-func hashDiagnostics(diags ...*source.Diagnostic) string {
+func hashDiagnostics(diags ...*cache.Diagnostic) string {
if len(diags) == 0 {
return emptyDiagnosticsHash
}
@@ -133,7 +133,7 @@
// computeDiagnosticHash should only be called from hashDiagnostics.
//
// TODO(rfindley): this should use source.Hash.
-func computeDiagnosticHash(diags ...*source.Diagnostic) string {
+func computeDiagnosticHash(diags ...*cache.Diagnostic) string {
source.SortDiagnostics(diags)
h := sha256.New()
for _, d := range diags {
@@ -302,7 +302,7 @@
}()
// common code for dispatching diagnostics
- store := func(dsource diagnosticSource, operation string, diagsByFile map[protocol.DocumentURI][]*source.Diagnostic, err error, merge bool) {
+ store := func(dsource diagnosticSource, operation string, diagsByFile map[protocol.DocumentURI][]*cache.Diagnostic, err error, merge bool) {
if err != nil {
event.Error(ctx, "warning: while "+operation, err, snapshot.Labels()...)
}
@@ -423,7 +423,7 @@
// the workspace). Otherwise, add a diagnostic to the file.
if diags, err := snapshot.OrphanedFileDiagnostics(ctx); err == nil {
for uri, diag := range diags {
- s.storeDiagnostics(snapshot, uri, orphanedSource, []*source.Diagnostic{diag}, true)
+ s.storeDiagnostics(snapshot, uri, orphanedSource, []*cache.Diagnostic{diag}, true)
}
} else {
if ctx.Err() == nil {
@@ -451,8 +451,8 @@
// operations.
var (
wg sync.WaitGroup
- pkgDiags map[protocol.DocumentURI][]*source.Diagnostic
- analysisDiags = make(map[protocol.DocumentURI][]*source.Diagnostic)
+ pkgDiags map[protocol.DocumentURI][]*cache.Diagnostic
+ analysisDiags = make(map[protocol.DocumentURI][]*cache.Diagnostic)
)
// Collect package diagnostics.
@@ -510,7 +510,7 @@
continue
}
tdiags := pkgDiags[uri]
- var tdiags2, adiags2 []*source.Diagnostic
+ var tdiags2, adiags2 []*cache.Diagnostic
source.CombineDiagnostics(tdiags, adiags, &tdiags2, &adiags2)
pkgDiags[uri] = tdiags2
s.storeDiagnostics(snapshot, uri, analysisSource, adiags2, true)
@@ -617,7 +617,7 @@
//
// TODO(hyangah): investigate whether we can unconditionally overwrite previous report.diags
// with the new diags and eliminate the need for the `merge` flag.
-func (s *server) storeDiagnostics(snapshot *cache.Snapshot, uri protocol.DocumentURI, dsource diagnosticSource, diags []*source.Diagnostic, merge bool) {
+func (s *server) storeDiagnostics(snapshot *cache.Snapshot, uri protocol.DocumentURI, dsource diagnosticSource, diags []*cache.Diagnostic, merge bool) {
// Safeguard: ensure that the file actually exists in the snapshot
// (see golang.org/issues/38602).
fh := snapshot.FindFile(uri)
@@ -643,7 +643,7 @@
return
}
if report.diags == nil || report.snapshotID != snapshot.GlobalID() || !merge {
- report.diags = map[string]*source.Diagnostic{}
+ report.diags = map[string]*cache.Diagnostic{}
}
report.snapshotID = snapshot.GlobalID()
for _, d := range diags {
@@ -666,7 +666,7 @@
// showCriticalErrorStatus shows the error as a progress report.
// If the error is nil, it clears any existing error progress report.
-func (s *server) showCriticalErrorStatus(ctx context.Context, snapshot *cache.Snapshot, err *source.CriticalError) {
+func (s *server) showCriticalErrorStatus(ctx context.Context, snapshot *cache.Snapshot, err *cache.CriticalError) {
s.criticalErrorStatusMu.Lock()
defer s.criticalErrorStatusMu.Unlock()
@@ -676,7 +676,7 @@
if err != nil {
event.Error(ctx, "errors loading workspace", err.MainError, snapshot.Labels()...)
for _, d := range err.Diagnostics {
- s.storeDiagnostics(snapshot, d.URI, modParseSource, []*source.Diagnostic{d}, true)
+ s.storeDiagnostics(snapshot, d.URI, modParseSource, []*cache.Diagnostic{d}, true)
}
errMsg = strings.ReplaceAll(err.MainError.Error(), "\n", " ")
}
@@ -725,12 +725,12 @@
anyReportsChanged := false
reportHashes := map[diagnosticSource]string{}
- var diags []*source.Diagnostic
+ var diags []*cache.Diagnostic
for dsource, report := range r.reports {
if report.snapshotID != snapshot.GlobalID() {
continue
}
- var reportDiags []*source.Diagnostic
+ var reportDiags []*cache.Diagnostic
for _, d := range report.diags {
diags = append(diags, d)
reportDiags = append(reportDiags, d)
@@ -791,7 +791,7 @@
}
}
-func toProtocolDiagnostics(diagnostics []*source.Diagnostic) []protocol.Diagnostic {
+func toProtocolDiagnostics(diagnostics []*cache.Diagnostic) []protocol.Diagnostic {
reports := []protocol.Diagnostic{}
for _, diag := range diagnostics {
pdiag := protocol.Diagnostic{
@@ -860,7 +860,7 @@
return ans
}
-func auxStr(v *source.Diagnostic, d *diagnosticReport, typ diagnosticSource) string {
+func auxStr(v *cache.Diagnostic, d *diagnosticReport, typ diagnosticSource) string {
// Tags? RelatedInformation?
msg := fmt.Sprintf("(%s)%q(source:%q,code:%q,severity:%s,snapshot:%d,type:%s)",
v.Range, v.Message, v.Source, v.Code, v.Severity, d.snapshotID, typ)
diff --git a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go
index b1c0964..b45a322 100644
--- a/gopls/internal/lsp/link.go
+++ b/gopls/internal/lsp/link.go
@@ -73,7 +73,7 @@
}
// Shift the start position to the location of the
// dependency within the require statement.
- target := source.BuildLink(snapshot.Options().LinkTarget, "mod/"+req.Mod.String(), "")
+ target := cache.BuildLink(snapshot.Options().LinkTarget, "mod/"+req.Mod.String(), "")
l, err := toProtocolLink(pm.Mapper, target, start+i, start+i+len(dep))
if err != nil {
return nil, err
@@ -148,7 +148,7 @@
if err != nil {
return nil, err
}
- targetURL := source.BuildLink(snapshot.Options().LinkTarget, urlPath, "")
+ targetURL := cache.BuildLink(snapshot.Options().LinkTarget, urlPath, "")
// Account for the quotation marks in the positions.
l, err := toProtocolLink(pgf.Mapper, targetURL, start+len(`"`), end-len(`"`))
if err != nil {
diff --git a/gopls/internal/lsp/mod/code_lens.go b/gopls/internal/lsp/mod/code_lens.go
index e53d586..6feb93e 100644
--- a/gopls/internal/lsp/mod/code_lens.go
+++ b/gopls/internal/lsp/mod/code_lens.go
@@ -12,6 +12,7 @@
"golang.org/x/mod/modfile"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/command"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/source"
@@ -27,7 +28,7 @@
}
}
-func upgradeLenses(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func upgradeLenses(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil || pm.File == nil {
return nil, err
@@ -88,7 +89,7 @@
}...), nil
}
-func tidyLens(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func tidyLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil || pm.File == nil {
return nil, err
@@ -108,7 +109,7 @@
}}, nil
}
-func vendorLens(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func vendorLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil || pm.File == nil {
return nil, err
@@ -136,7 +137,7 @@
return []protocol.CodeLens{{Range: rng, Command: &cmd}}, nil
}
-func moduleStmtRange(fh file.Handle, pm *source.ParsedModule) (protocol.Range, error) {
+func moduleStmtRange(fh file.Handle, pm *cache.ParsedModule) (protocol.Range, error) {
if pm.File == nil || pm.File.Module == nil || pm.File.Module.Syntax == nil {
return protocol.Range{}, fmt.Errorf("no module statement in %s", fh.URI())
}
@@ -146,7 +147,7 @@
// firstRequireRange returns the range for the first "require" in the given
// go.mod file. This is either a require block or an individual require line.
-func firstRequireRange(fh file.Handle, pm *source.ParsedModule) (protocol.Range, error) {
+func firstRequireRange(fh file.Handle, pm *cache.ParsedModule) (protocol.Range, error) {
if len(pm.File.Require) == 0 {
return protocol.Range{}, fmt.Errorf("no requires in the file %s", fh.URI())
}
@@ -165,7 +166,7 @@
return pm.Mapper.OffsetRange(start.Byte, end.Byte)
}
-func vulncheckLenses(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func vulncheckLenses(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil || pm.File == nil {
return nil, err
diff --git a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagnostics.go
index d524134..2c33181 100644
--- a/gopls/internal/lsp/mod/diagnostics.go
+++ b/gopls/internal/lsp/mod/diagnostics.go
@@ -21,14 +21,13 @@
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/command"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/gopls/internal/vulncheck/govulncheck"
"golang.org/x/tools/internal/event"
)
// Diagnostics returns diagnostics from parsing the modules in the workspace.
-func Diagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func Diagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
ctx, done := event.Start(ctx, "mod.Diagnostics", snapshot.Labels()...)
defer done()
@@ -36,7 +35,7 @@
}
// Diagnostics returns diagnostics from running go mod tidy.
-func TidyDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func TidyDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
ctx, done := event.Start(ctx, "mod.Diagnostics", snapshot.Labels()...)
defer done()
@@ -45,7 +44,7 @@
// UpgradeDiagnostics returns upgrade diagnostics for the modules in the
// workspace with known upgrades.
-func UpgradeDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func UpgradeDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
ctx, done := event.Start(ctx, "mod.UpgradeDiagnostics", snapshot.Labels()...)
defer done()
@@ -54,20 +53,20 @@
// VulnerabilityDiagnostics returns vulnerability diagnostics for the active modules in the
// workspace with known vulnerabilities.
-func VulnerabilityDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func VulnerabilityDiagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
ctx, done := event.Start(ctx, "mod.VulnerabilityDiagnostics", snapshot.Labels()...)
defer done()
return collectDiagnostics(ctx, snapshot, ModVulnerabilityDiagnostics)
}
-func collectDiagnostics(ctx context.Context, snapshot *cache.Snapshot, diagFn func(context.Context, *cache.Snapshot, file.Handle) ([]*source.Diagnostic, error)) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func collectDiagnostics(ctx context.Context, snapshot *cache.Snapshot, diagFn func(context.Context, *cache.Snapshot, file.Handle) ([]*cache.Diagnostic, error)) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
g, ctx := errgroup.WithContext(ctx)
cpulimit := runtime.GOMAXPROCS(0)
g.SetLimit(cpulimit)
var mu sync.Mutex
- reports := make(map[protocol.DocumentURI][]*source.Diagnostic)
+ reports := make(map[protocol.DocumentURI][]*cache.Diagnostic)
for _, uri := range snapshot.ModFiles() {
uri := uri
@@ -96,7 +95,7 @@
}
// ModParseDiagnostics reports diagnostics from parsing the mod file.
-func ModParseDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (diagnostics []*source.Diagnostic, err error) {
+func ModParseDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (diagnostics []*cache.Diagnostic, err error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
if pm == nil || len(pm.ParseErrors) == 0 {
@@ -108,7 +107,7 @@
}
// ModTidyDiagnostics reports diagnostics from running go mod tidy.
-func ModTidyDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (diagnostics []*source.Diagnostic, err error) {
+func ModTidyDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (diagnostics []*cache.Diagnostic, err error) {
pm, err := snapshot.ParseMod(ctx, fh) // memoized
if err != nil {
return nil, nil // errors reported by ModDiagnostics above
@@ -136,7 +135,7 @@
// ModUpgradeDiagnostics adds upgrade quick fixes for individual modules if the upgrades
// are recorded in the view.
-func ModUpgradeDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (upgradeDiagnostics []*source.Diagnostic, err error) {
+func ModUpgradeDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (upgradeDiagnostics []*cache.Diagnostic, err error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
// Don't return an error if there are parse error diagnostics to be shown, but also do not
@@ -167,13 +166,13 @@
if err != nil {
return nil, err
}
- upgradeDiagnostics = append(upgradeDiagnostics, &source.Diagnostic{
+ upgradeDiagnostics = append(upgradeDiagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityInformation,
- Source: source.UpgradeNotification,
+ Source: cache.UpgradeNotification,
Message: fmt.Sprintf("%v can be upgraded", req.Mod.Path),
- SuggestedFixes: []source.SuggestedFix{cache.SuggestedFixFromCommand(cmd, protocol.QuickFix)},
+ SuggestedFixes: []cache.SuggestedFix{cache.SuggestedFixFromCommand(cmd, protocol.QuickFix)},
})
}
@@ -184,7 +183,7 @@
// ModVulnerabilityDiagnostics adds diagnostics for vulnerabilities in individual modules
// if the vulnerability is recorded in the view.
-func ModVulnerabilityDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (vulnDiagnostics []*source.Diagnostic, err error) {
+func ModVulnerabilityDiagnostics(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) (vulnDiagnostics []*cache.Diagnostic, err error) {
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
// Don't return an error if there are parse error diagnostics to be shown, but also do not
@@ -195,20 +194,20 @@
return nil, err
}
- diagSource := source.Govulncheck
+ diagSource := cache.Govulncheck
vs := snapshot.Vulnerabilities(fh.URI())[fh.URI()]
if vs == nil && snapshot.Options().Vulncheck == settings.ModeVulncheckImports {
vs, err = snapshot.ModVuln(ctx, fh.URI())
if err != nil {
return nil, err
}
- diagSource = source.Vulncheck
+ diagSource = cache.Vulncheck
}
if vs == nil || len(vs.Findings) == 0 {
return nil, nil
}
- suggestRunOrResetGovulncheck, err := suggestGovulncheckAction(diagSource == source.Govulncheck, fh.URI())
+ suggestRunOrResetGovulncheck, err := suggestGovulncheckAction(diagSource == cache.Govulncheck, fh.URI())
if err != nil {
// must not happen
return nil, err // TODO: bug report
@@ -240,7 +239,7 @@
// Map affecting vulns to 'warning' level diagnostics,
// others to 'info' level diagnostics.
// Fixes will include only the upgrades for warning level diagnostics.
- var warningFixes, infoFixes []source.SuggestedFix
+ var warningFixes, infoFixes []cache.SuggestedFix
var warningSet, infoSet = map[string]bool{}, map[string]bool{}
for _, finding := range findings {
// It is possible that the source code was changed since the last
@@ -311,24 +310,24 @@
if len(warningSet) > 0 {
warning := sortedKeys(warningSet)
warningFixes = append(warningFixes, suggestRunOrResetGovulncheck)
- vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{
+ vulnDiagnostics = append(vulnDiagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityWarning,
Source: diagSource,
- Message: getVulnMessage(req.Mod.Path, warning, true, diagSource == source.Govulncheck),
+ Message: getVulnMessage(req.Mod.Path, warning, true, diagSource == cache.Govulncheck),
SuggestedFixes: warningFixes,
})
}
if len(infoSet) > 0 {
info := sortedKeys(infoSet)
infoFixes = append(infoFixes, suggestRunOrResetGovulncheck)
- vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{
+ vulnDiagnostics = append(vulnDiagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityInformation,
Source: diagSource,
- Message: getVulnMessage(req.Mod.Path, info, false, diagSource == source.Govulncheck),
+ Message: getVulnMessage(req.Mod.Path, info, false, diagSource == cache.Govulncheck),
SuggestedFixes: infoFixes,
})
}
@@ -367,13 +366,13 @@
}
if len(warningSet) > 0 {
warning := sortedKeys(warningSet)
- fixes := []source.SuggestedFix{suggestRunOrResetGovulncheck}
- vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{
+ fixes := []cache.SuggestedFix{suggestRunOrResetGovulncheck}
+ vulnDiagnostics = append(vulnDiagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityWarning,
Source: diagSource,
- Message: getVulnMessage("go", warning, true, diagSource == source.Govulncheck),
+ Message: getVulnMessage("go", warning, true, diagSource == cache.Govulncheck),
SuggestedFixes: fixes,
})
@@ -386,13 +385,13 @@
}
if len(infoSet) > 0 {
info := sortedKeys(infoSet)
- fixes := []source.SuggestedFix{suggestRunOrResetGovulncheck}
- vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{
+ fixes := []cache.SuggestedFix{suggestRunOrResetGovulncheck}
+ vulnDiagnostics = append(vulnDiagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityInformation,
Source: diagSource,
- Message: getVulnMessage("go", info, false, diagSource == source.Govulncheck),
+ Message: getVulnMessage("go", info, false, diagSource == cache.Govulncheck),
SuggestedFixes: fixes,
})
}
@@ -445,14 +444,14 @@
// for more accurate investigation (if the present vulncheck diagnostics are based on
// analysis less accurate than govulncheck) or reset the existing govulncheck result
// (if the present vulncheck diagnostics are already based on govulncheck run).
-func suggestGovulncheckAction(fromGovulncheck bool, uri protocol.DocumentURI) (source.SuggestedFix, error) {
+func suggestGovulncheckAction(fromGovulncheck bool, uri protocol.DocumentURI) (cache.SuggestedFix, error) {
if fromGovulncheck {
resetVulncheck, err := command.NewResetGoModDiagnosticsCommand("Reset govulncheck result", command.ResetGoModDiagnosticsArgs{
URIArg: command.URIArg{URI: uri},
- DiagnosticSource: string(source.Govulncheck),
+ DiagnosticSource: string(cache.Govulncheck),
})
if err != nil {
- return source.SuggestedFix{}, err
+ return cache.SuggestedFix{}, err
}
return cache.SuggestedFixFromCommand(resetVulncheck, protocol.QuickFix), nil
}
@@ -461,7 +460,7 @@
Pattern: "./...",
})
if err != nil {
- return source.SuggestedFix{}, err
+ return cache.SuggestedFix{}, err
}
return cache.SuggestedFixFromCommand(vulncheck, protocol.QuickFix), nil
}
diff --git a/gopls/internal/lsp/mod/format.go b/gopls/internal/lsp/mod/format.go
index 7b0c6b1..b3eaad3 100644
--- a/gopls/internal/lsp/mod/format.go
+++ b/gopls/internal/lsp/mod/format.go
@@ -8,12 +8,12 @@
"context"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
-func Format(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
+func Format(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
ctx, done := event.Start(ctx, "mod.Format")
defer done()
diff --git a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go
index eadaa0f..1b68659 100644
--- a/gopls/internal/lsp/mod/hover.go
+++ b/gopls/internal/lsp/mod/hover.go
@@ -16,7 +16,6 @@
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/gopls/internal/vulncheck"
"golang.org/x/tools/gopls/internal/vulncheck/govulncheck"
@@ -58,7 +57,7 @@
return hoverOnRequireStatement(ctx, pm, offset, snapshot, fh)
}
-func hoverOnRequireStatement(ctx context.Context, pm *source.ParsedModule, offset int, snapshot *cache.Snapshot, fh file.Handle) (*protocol.Hover, error) {
+func hoverOnRequireStatement(ctx context.Context, pm *cache.ParsedModule, offset int, snapshot *cache.Snapshot, fh file.Handle) (*protocol.Hover, error) {
// Confirm that the cursor is at the position of a require statement.
var req *modfile.Require
var startOffset, endOffset int
@@ -129,7 +128,7 @@
}, nil
}
-func hoverOnModuleStatement(ctx context.Context, pm *source.ParsedModule, offset int, snapshot *cache.Snapshot, fh file.Handle) (*protocol.Hover, bool) {
+func hoverOnModuleStatement(ctx context.Context, pm *cache.ParsedModule, offset int, snapshot *cache.Snapshot, fh file.Handle) (*protocol.Hover, bool) {
module := pm.File.Module
if module == nil {
return nil, false // no module stmt
@@ -351,7 +350,7 @@
if strings.ToLower(options.LinkTarget) == "pkg.go.dev" {
target = strings.Replace(target, req.Mod.Path, req.Mod.String(), 1)
}
- reference = fmt.Sprintf("[%s](%s)", imp, source.BuildLink(options.LinkTarget, target, ""))
+ reference = fmt.Sprintf("[%s](%s)", imp, cache.BuildLink(options.LinkTarget, target, ""))
}
b.WriteString("This module is necessary because " + reference + " is imported in")
diff --git a/gopls/internal/lsp/mod/inlayhint.go b/gopls/internal/lsp/mod/inlayhint.go
index 2fda62f..2a3a5ba 100644
--- a/gopls/internal/lsp/mod/inlayhint.go
+++ b/gopls/internal/lsp/mod/inlayhint.go
@@ -9,11 +9,11 @@
"golang.org/x/mod/modfile"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
)
-func InlayHint(ctx context.Context, snapshot source.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.InlayHint, error) {
+func InlayHint(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.InlayHint, error) {
// Inlay hints are enabled if the client supports them.
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
diff --git a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go
index ddbb03a..093b142 100644
--- a/gopls/internal/lsp/semantic.go
+++ b/gopls/internal/lsp/semantic.go
@@ -20,6 +20,7 @@
"golang.org/x/tools/gopls/internal/astutil"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -232,7 +233,7 @@
pgf *source.ParsedGoFile
start, end token.Pos // range of interest
ti *types.Info
- pkg source.Package
+ pkg *cache.Package
fset *token.FileSet
// path from the root of the parse tree, used for debugging
stack []ast.Node
diff --git a/gopls/internal/lsp/server.go b/gopls/internal/lsp/server.go
index fe76063..0c187bd 100644
--- a/gopls/internal/lsp/server.go
+++ b/gopls/internal/lsp/server.go
@@ -182,7 +182,7 @@
// efficient to compute the set of packages and TypeCheck and
// Analyze them all at once. Or instead support textDocument/diagnostic
// (golang/go#60122).
-func (s *server) diagnoseFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (file.Handle, []*source.Diagnostic, error) {
+func (s *server) diagnoseFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (file.Handle, []*cache.Diagnostic, error) {
fh, err := snapshot.ReadFile(ctx, uri)
if err != nil {
return nil, nil, err
@@ -199,7 +199,7 @@
if err != nil {
return nil, nil, err
}
- var td, ad []*source.Diagnostic // combine load/parse/type + analysis diagnostics
+ var td, ad []*cache.Diagnostic // combine load/parse/type + analysis diagnostics
source.CombineDiagnostics(pkgDiags, adiags[uri], &td, &ad)
s.storeDiagnostics(snapshot, uri, typeCheckSource, td, true)
s.storeDiagnostics(snapshot, uri, analysisSource, ad, true)
diff --git a/gopls/internal/lsp/source/add_import.go b/gopls/internal/lsp/source/add_import.go
index 8930e8f..90d136d 100644
--- a/gopls/internal/lsp/source/add_import.go
+++ b/gopls/internal/lsp/source/add_import.go
@@ -8,12 +8,13 @@
"context"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/imports"
)
// AddImport adds a single import statement to the given file
-func AddImport(ctx context.Context, snapshot Snapshot, fh file.Handle, importPath string) ([]protocol.TextEdit, error) {
+func AddImport(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, importPath string) ([]protocol.TextEdit, error) {
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/source/call_hierarchy.go
index 606ae30..698d0c7 100644
--- a/gopls/internal/lsp/source/call_hierarchy.go
+++ b/gopls/internal/lsp/source/call_hierarchy.go
@@ -16,6 +16,7 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/internal/event"
@@ -23,7 +24,7 @@
)
// PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file.
-func PrepareCallHierarchy(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.CallHierarchyItem, error) {
+func PrepareCallHierarchy(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.CallHierarchyItem, error) {
ctx, done := event.Start(ctx, "source.PrepareCallHierarchy")
defer done()
@@ -64,7 +65,7 @@
}
// IncomingCalls returns an array of CallHierarchyIncomingCall for a file and the position within the file.
-func IncomingCalls(ctx context.Context, snapshot Snapshot, fh file.Handle, pos protocol.Position) ([]protocol.CallHierarchyIncomingCall, error) {
+func IncomingCalls(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pos protocol.Position) ([]protocol.CallHierarchyIncomingCall, error) {
ctx, done := event.Start(ctx, "source.IncomingCalls")
defer done()
@@ -105,7 +106,7 @@
}
// enclosingNodeCallItem creates a CallHierarchyItem representing the function call at loc.
-func enclosingNodeCallItem(ctx context.Context, snapshot Snapshot, pkgPath PackagePath, loc protocol.Location) (protocol.CallHierarchyItem, error) {
+func enclosingNodeCallItem(ctx context.Context, snapshot *cache.Snapshot, pkgPath PackagePath, loc protocol.Location) (protocol.CallHierarchyItem, error) {
// Parse the file containing the reference.
fh, err := snapshot.ReadFile(ctx, loc.URI)
if err != nil {
@@ -178,7 +179,7 @@
}
// OutgoingCalls returns an array of CallHierarchyOutgoingCall for a file and the position within the file.
-func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.CallHierarchyOutgoingCall, error) {
+func OutgoingCalls(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.CallHierarchyOutgoingCall, error) {
ctx, done := event.Start(ctx, "source.OutgoingCalls")
defer done()
diff --git a/gopls/internal/lsp/source/change_signature.go b/gopls/internal/lsp/source/change_signature.go
index 5f30629..cb79765 100644
--- a/gopls/internal/lsp/source/change_signature.go
+++ b/gopls/internal/lsp/source/change_signature.go
@@ -18,6 +18,8 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
+ "golang.org/x/tools/gopls/internal/lsp/cache/parsego"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/imports"
@@ -38,7 +40,7 @@
// - Improve the extra newlines in output.
// - Stream type checking via ForEachPackage.
// - Avoid unnecessary additional type checking.
-func RemoveUnusedParameter(ctx context.Context, fh file.Handle, rng protocol.Range, snapshot Snapshot) ([]protocol.DocumentChanges, error) {
+func RemoveUnusedParameter(ctx context.Context, fh file.Handle, rng protocol.Range, snapshot *cache.Snapshot) ([]protocol.DocumentChanges, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, err
@@ -304,9 +306,9 @@
//
// See rewriteCalls for more details.
type signatureRewrite struct {
- snapshot Snapshot
- pkg Package
- pgf *ParsedGoFile
+ snapshot *cache.Snapshot
+ pkg *cache.Package
+ pgf *parsego.File
origDecl, newDecl *ast.FuncDecl
params *ast.FieldList
callArgs []ast.Expr
@@ -436,7 +438,7 @@
// If expectErrors is true, reTypeCheck allows errors in the new package.
// TODO(rfindley): perhaps this should be a filter to specify which errors are
// acceptable.
-func reTypeCheck(logf func(string, ...any), orig Package, fileMask map[protocol.DocumentURI]*ast.File, expectErrors bool) (*types.Package, *types.Info, error) {
+func reTypeCheck(logf func(string, ...any), orig *cache.Package, fileMask map[protocol.DocumentURI]*ast.File, expectErrors bool) (*types.Package, *types.Info, error) {
pkg := types.NewPackage(string(orig.Metadata().PkgPath), string(orig.Metadata().Name))
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
diff --git a/gopls/internal/lsp/source/code_lens.go b/gopls/internal/lsp/source/code_lens.go
index 28495f0..ea1fafd 100644
--- a/gopls/internal/lsp/source/code_lens.go
+++ b/gopls/internal/lsp/source/code_lens.go
@@ -14,11 +14,12 @@
"strings"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/command"
"golang.org/x/tools/gopls/internal/lsp/protocol"
)
-type LensFunc func(context.Context, Snapshot, file.Handle) ([]protocol.CodeLens, error)
+type LensFunc func(context.Context, *cache.Snapshot, file.Handle) ([]protocol.CodeLens, error)
// LensFuncs returns the supported lensFuncs for Go files.
func LensFuncs() map[command.Command]LensFunc {
@@ -35,7 +36,7 @@
benchmarkRe = regexp.MustCompile(`^Benchmark([^a-z]|$)`)
)
-func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func runTestCodeLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
var codeLens []protocol.CodeLens
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
@@ -98,7 +99,7 @@
Benchmarks []TestFn
}
-func TestsAndBenchmarks(pkg Package, pgf *ParsedGoFile) (TestFns, error) {
+func TestsAndBenchmarks(pkg *cache.Package, pgf *ParsedGoFile) (TestFns, error) {
var out TestFns
if !strings.HasSuffix(pgf.URI.Path(), "_test.go") {
@@ -128,7 +129,7 @@
return out, nil
}
-func matchTestFunc(fn *ast.FuncDecl, pkg Package, nameRe *regexp.Regexp, paramID string) bool {
+func matchTestFunc(fn *ast.FuncDecl, pkg *cache.Package, nameRe *regexp.Regexp, paramID string) bool {
// Make sure that the function name matches a test function.
if !nameRe.MatchString(fn.Name.Name) {
return false
@@ -166,7 +167,7 @@
return namedObj.Id() == paramID
}
-func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func goGenerateCodeLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
if err != nil {
return nil, err
@@ -200,7 +201,7 @@
return nil, nil
}
-func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func regenerateCgoLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
if err != nil {
return nil, err
@@ -226,7 +227,7 @@
return []protocol.CodeLens{{Range: rng, Command: &cmd}}, nil
}
-func toggleDetailsCodeLens(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
+func toggleDetailsCodeLens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.CodeLens, error) {
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go
index de0fa92..dcd6a3e 100644
--- a/gopls/internal/lsp/source/completion/completion.go
+++ b/gopls/internal/lsp/source/completion/completion.go
@@ -29,6 +29,7 @@
"golang.org/x/tools/go/ast/astutil"
goplsastutil "golang.org/x/tools/gopls/internal/astutil"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/gopls/internal/lsp/snippet"
@@ -167,8 +168,8 @@
// completer contains the necessary information for a single completion request.
type completer struct {
- snapshot source.Snapshot
- pkg source.Package
+ snapshot *cache.Snapshot
+ pkg *cache.Package
qf types.Qualifier // for qualifying typed expressions
mq source.MetadataQualifier // for syntactic qualifying
opts *completionOptions
@@ -443,7 +444,7 @@
// The selection is computed based on the preceding identifier and can be used by
// the client to score the quality of the completion. For instance, some clients
// may tolerate imperfect matches as valid completion results, since users may make typos.
-func Completion(ctx context.Context, snapshot source.Snapshot, fh file.Handle, protoPos protocol.Position, protoContext protocol.CompletionContext) ([]CompletionItem, *Selection, error) {
+func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, protoPos protocol.Position, protoContext protocol.CompletionContext) ([]CompletionItem, *Selection, error) {
ctx, done := event.Start(ctx, "completion.Completion")
defer done()
diff --git a/gopls/internal/lsp/source/completion/package.go b/gopls/internal/lsp/source/completion/package.go
index d4aacf4..b5e2e26 100644
--- a/gopls/internal/lsp/source/completion/package.go
+++ b/gopls/internal/lsp/source/completion/package.go
@@ -19,6 +19,7 @@
"unicode"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/gopls/internal/lsp/source"
@@ -27,7 +28,7 @@
// packageClauseCompletions offers completions for a package declaration when
// one is not present in the given file.
-func packageClauseCompletions(ctx context.Context, snapshot source.Snapshot, fh file.Handle, position protocol.Position) ([]CompletionItem, *Selection, error) {
+func packageClauseCompletions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) ([]CompletionItem, *Selection, error) {
// We know that the AST for this file will be empty due to the missing
// package declaration, but parse it anyway to get a mapper.
// TODO(adonovan): opt: there's no need to parse just to get a mapper.
@@ -202,7 +203,7 @@
// have the given prefix and are used in the same directory as the given
// file. This also includes test packages for these packages (<pkg>_test) and
// the directory name itself.
-func packageSuggestions(ctx context.Context, snapshot source.Snapshot, fileURI protocol.DocumentURI, prefix string) (packages []candidate, err error) {
+func packageSuggestions(ctx context.Context, snapshot *cache.Snapshot, fileURI protocol.DocumentURI, prefix string) (packages []candidate, err error) {
active, err := snapshot.WorkspaceMetadata(ctx)
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/source/completion/statements.go b/gopls/internal/lsp/source/completion/statements.go
index a801a09..acbc929 100644
--- a/gopls/internal/lsp/source/completion/statements.go
+++ b/gopls/internal/lsp/source/completion/statements.go
@@ -10,6 +10,7 @@
"go/token"
"go/types"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/snippet"
"golang.org/x/tools/gopls/internal/lsp/source"
@@ -320,7 +321,7 @@
// returns "b" etc. An empty string indicates that the function signature
// does not take a testing.TB parameter or does so but is ignored such
// as func someFunc(*testing.T).
-func getTestVar(enclosingFunc *funcInfo, pkg source.Package) string {
+func getTestVar(enclosingFunc *funcInfo, pkg *cache.Package) string {
if enclosingFunc == nil || enclosingFunc.sig == nil {
return ""
}
diff --git a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/definition.go
index 4df5f6b..89a9f87 100644
--- a/gopls/internal/lsp/source/definition.go
+++ b/gopls/internal/lsp/source/definition.go
@@ -15,12 +15,13 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)
// Definition handles the textDocument/definition request for Go files.
-func Definition(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
+func Definition(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
ctx, done := event.Start(ctx, "source.Definition")
defer done()
@@ -93,7 +94,7 @@
// builtinDefinition returns the location of the fake source
// declaration of a built-in in {builtin,unsafe}.go.
-func builtinDefinition(ctx context.Context, snapshot Snapshot, obj types.Object) ([]protocol.Location, error) {
+func builtinDefinition(ctx context.Context, snapshot *cache.Snapshot, obj types.Object) ([]protocol.Location, error) {
// getDecl returns the file-level declaration of name
// using legacy (go/ast) object resolution.
getDecl := func(file *ast.File, name string) (ast.Node, error) {
@@ -184,7 +185,7 @@
// TODO(rfindley): this function exists to preserve the pre-existing behavior
// of source.Identifier. Eliminate this helper in favor of sharing
// functionality with objectsAt, after choosing suitable primitives.
-func referencedObject(pkg Package, pgf *ParsedGoFile, pos token.Pos) (*ast.Ident, types.Object, types.Type) {
+func referencedObject(pkg *cache.Package, pgf *ParsedGoFile, pos token.Pos) (*ast.Ident, types.Object, types.Type) {
path := pathEnclosingObjNode(pgf.File, pos)
if len(path) == 0 {
return nil, nil, nil
@@ -224,7 +225,7 @@
// import spec containing pos.
//
// If pos is not inside an import spec, it returns nil, nil.
-func importDefinition(ctx context.Context, s Snapshot, pkg Package, pgf *ParsedGoFile, pos token.Pos) ([]protocol.Location, error) {
+func importDefinition(ctx context.Context, s *cache.Snapshot, pkg *cache.Package, pgf *ParsedGoFile, pos token.Pos) ([]protocol.Location, error) {
var imp *ast.ImportSpec
for _, spec := range pgf.File.Imports {
// We use "<= End" to accept a query immediately after an ImportSpec.
diff --git a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source/diagnostics.go
index 8ca78c7..06c8c8c 100644
--- a/gopls/internal/lsp/source/diagnostics.go
+++ b/gopls/internal/lsp/source/diagnostics.go
@@ -7,23 +7,17 @@
import (
"context"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/progress"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
)
-type SuggestedFix struct {
- Title string
- Edits map[protocol.DocumentURI][]protocol.TextEdit
- Command *protocol.Command
- ActionKind protocol.CodeActionKind
-}
-
// Analyze reports go/analysis-framework diagnostics in the specified package.
//
// If the provided tracker is non-nil, it may be used to provide notifications
// of the ongoing analysis pass.
-func Analyze(ctx context.Context, snapshot Snapshot, pkgIDs map[PackageID]unit, tracker *progress.Tracker) (map[protocol.DocumentURI][]*Diagnostic, error) {
+func Analyze(ctx context.Context, snapshot *cache.Snapshot, pkgIDs map[PackageID]unit, tracker *progress.Tracker) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
// Exit early if the context has been canceled. This also protects us
// from a race on Options, see golang/go#36699.
if ctx.Err() != nil {
@@ -50,7 +44,7 @@
}
// Report diagnostics and errors from root analyzers.
- reports := make(map[protocol.DocumentURI][]*Diagnostic)
+ reports := make(map[protocol.DocumentURI][]*cache.Diagnostic)
for _, diag := range analysisDiagnostics {
reports[diag.URI] = append(reports[diag.URI], diag)
}
@@ -77,7 +71,7 @@
// easily choose whether to keep the results separate or combined.
//
// The arguments are not modified.
-func CombineDiagnostics(tdiags []*Diagnostic, adiags []*Diagnostic, outT, outA *[]*Diagnostic) {
+func CombineDiagnostics(tdiags []*cache.Diagnostic, adiags []*cache.Diagnostic, outT, outA *[]*cache.Diagnostic) {
// Build index of (list+parse+)type errors.
type key struct {
diff --git a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go
index b44a25d..bc3d37d 100644
--- a/gopls/internal/lsp/source/fix.go
+++ b/gopls/internal/lsp/source/fix.go
@@ -14,9 +14,9 @@
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
- "golang.org/x/tools/gopls/internal/lsp/analysis/embeddirective"
"golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct"
"golang.org/x/tools/gopls/internal/lsp/analysis/undeclaredname"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/internal/imports"
@@ -31,14 +31,7 @@
// TODO(rfindley): the signature of suggestedFixFunc should probably accept
// (context.Context, Snapshot, protocol.Diagnostic). No reason for us to
// encode as a (URI, Range) pair when we have the protocol type.
- suggestedFixFunc func(context.Context, Snapshot, file.Handle, protocol.Range) ([]protocol.TextDocumentEdit, error)
- suggestedFixer struct {
- // fixesDiagnostic reports if a diagnostic from the analyzer can be fixed
- // by Fix. If nil then all diagnostics from the analyzer are assumed to be
- // fixable.
- canFix func(*Diagnostic) bool
- fix suggestedFixFunc
- }
+ suggestedFixFunc func(context.Context, *cache.Snapshot, file.Handle, protocol.Range) ([]protocol.TextDocumentEdit, error)
)
// suggestedFixes maps a suggested fix command id to its handler.
@@ -55,26 +48,23 @@
// (just calling RangePos) that we can push it down into each singleFile fixer.
// All the fixers will then have a common and fully general interface, instead
// of the current two-tier system.
-var suggestedFixes = map[settings.Fix]suggestedFixer{
- settings.FillStruct: {fix: singleFile(fillstruct.SuggestedFix)},
- settings.UndeclaredName: {fix: singleFile(undeclaredname.SuggestedFix)},
- settings.ExtractVariable: {fix: singleFile(extractVariable)},
- settings.InlineCall: {fix: inlineCall},
- settings.ExtractFunction: {fix: singleFile(extractFunction)},
- settings.ExtractMethod: {fix: singleFile(extractMethod)},
- settings.InvertIfCondition: {fix: singleFile(invertIfCondition)},
- settings.StubMethods: {fix: stubSuggestedFixFunc},
- settings.AddEmbedImport: {
- canFix: fixedByImportingEmbed,
- fix: addEmbedImport,
- },
+var suggestedFixes = map[settings.Fix]suggestedFixFunc{
+ settings.FillStruct: singleFile(fillstruct.SuggestedFix),
+ settings.UndeclaredName: singleFile(undeclaredname.SuggestedFix),
+ settings.ExtractVariable: singleFile(extractVariable),
+ settings.InlineCall: inlineCall,
+ settings.ExtractFunction: singleFile(extractFunction),
+ settings.ExtractMethod: singleFile(extractMethod),
+ settings.InvertIfCondition: singleFile(invertIfCondition),
+ settings.StubMethods: stubSuggestedFixFunc,
+ settings.AddEmbedImport: addEmbedImport,
}
type singleFileFixFunc func(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error)
// singleFile calls analyzers that expect inputs for a single file.
func singleFile(sf singleFileFixFunc) suggestedFixFunc {
- return func(ctx context.Context, snapshot Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
+ return func(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, err
@@ -94,34 +84,17 @@
}
}
-// CanFix returns true if Analyzer.Fix can fix the Diagnostic.
-//
-// It returns true by default: only if the analyzer is configured explicitly to
-// ignore this diagnostic does it return false.
-//
-// TODO(rfindley): reconcile the semantics of 'Fix' and
-// 'suggestedAnalysisFixes'.
-func CanFix(a *settings.Analyzer, d *Diagnostic) bool {
- fixer, ok := suggestedFixes[a.Fix]
- if !ok || fixer.canFix == nil {
- // See the above TODO: this doesn't make sense, but preserves pre-existing
- // semantics.
- return true
- }
- return fixer.canFix(d)
-}
-
// ApplyFix applies the command's suggested fix to the given file and
// range, returning the resulting edits.
-func ApplyFix(ctx context.Context, fix settings.Fix, snapshot Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
+func ApplyFix(ctx context.Context, fix settings.Fix, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
fixer, ok := suggestedFixes[fix]
if !ok {
return nil, fmt.Errorf("no suggested fix function for %s", fix)
}
- return fixer.fix(ctx, snapshot, fh, rng)
+ return fixer(ctx, snapshot, fh, rng)
}
-func suggestedFixToEdits(ctx context.Context, snapshot Snapshot, fset *token.FileSet, suggestion *analysis.SuggestedFix) ([]protocol.TextDocumentEdit, error) {
+func suggestedFixToEdits(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, suggestion *analysis.SuggestedFix) ([]protocol.TextDocumentEdit, error) {
editsPerFile := map[protocol.DocumentURI]*protocol.TextDocumentEdit{}
for _, edit := range suggestion.TextEdits {
tokFile := fset.File(edit.Pos)
@@ -169,16 +142,8 @@
return edits, nil
}
-// fixedByImportingEmbed returns true if diag can be fixed by addEmbedImport.
-func fixedByImportingEmbed(diag *Diagnostic) bool {
- if diag == nil {
- return false
- }
- return diag.Message == embeddirective.MissingImportMessage
-}
-
// addEmbedImport adds a missing embed "embed" import with blank name.
-func addEmbedImport(ctx context.Context, snapshot Snapshot, fh file.Handle, _ protocol.Range) ([]protocol.TextDocumentEdit, error) {
+func addEmbedImport(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, _ protocol.Range) ([]protocol.TextDocumentEdit, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, fmt.Errorf("narrow pkg: %w", err)
diff --git a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/source/folding_range.go
index b6f7faa..14961e5 100644
--- a/gopls/internal/lsp/source/folding_range.go
+++ b/gopls/internal/lsp/source/folding_range.go
@@ -13,6 +13,7 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
)
@@ -24,7 +25,7 @@
}
// FoldingRange gets all of the folding range for f.
-func FoldingRange(ctx context.Context, snapshot Snapshot, fh file.Handle, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
+func FoldingRange(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, lineFoldingOnly bool) (ranges []*FoldingRangeInfo, err error) {
// TODO(suzmue): consider limiting the number of folding ranges returned, and
// implement a way to prioritize folding ranges in that case.
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
diff --git a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/format.go
index d36d50b..e199023 100644
--- a/gopls/internal/lsp/source/format.go
+++ b/gopls/internal/lsp/source/format.go
@@ -17,6 +17,7 @@
"text/scanner"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/internal/diff"
@@ -26,7 +27,7 @@
)
// Format formats a file with a given range.
-func Format(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
+func Format(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
ctx, done := event.Start(ctx, "source.Format")
defer done()
@@ -109,7 +110,7 @@
// In addition to returning the result of applying all edits,
// it returns a list of fixes that could be applied to the file, with the
// corresponding TextEdits that would be needed to apply that fix.
-func AllImportsFixes(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
+func AllImportsFixes(ctx context.Context, snapshot *cache.Snapshot, pgf *ParsedGoFile) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
ctx, done := event.Start(ctx, "source.AllImportsFixes")
defer done()
@@ -124,7 +125,7 @@
// computeImportEdits computes a set of edits that perform one or all of the
// necessary import fixes.
-func computeImportEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
+func computeImportEdits(ctx context.Context, snapshot *cache.Snapshot, pgf *ParsedGoFile, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) {
filename := pgf.URI.Path()
// Build up basic information about the original file.
@@ -154,7 +155,7 @@
}
// ComputeOneImportFixEdits returns text edits for a single import fix.
-func ComputeOneImportFixEdits(snapshot Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
+func ComputeOneImportFixEdits(snapshot *cache.Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
options := &imports.Options{
LocalPrefix: snapshot.Options().Local,
// Defaults.
@@ -168,7 +169,7 @@
return computeFixEdits(snapshot, pgf, options, []*imports.ImportFix{fix})
}
-func computeFixEdits(snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
+func computeFixEdits(snapshot *cache.Snapshot, pgf *ParsedGoFile, options *imports.Options, fixes []*imports.ImportFix) ([]protocol.TextEdit, error) {
// trim the original data to match fixedData
left, err := importPrefix(pgf.Src)
if err != nil {
@@ -301,7 +302,7 @@
return 0
}
-func computeTextEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, formatted string) ([]protocol.TextEdit, error) {
+func computeTextEdits(ctx context.Context, snapshot *cache.Snapshot, pgf *ParsedGoFile, formatted string) ([]protocol.TextEdit, error) {
_, done := event.Start(ctx, "source.computeTextEdits")
defer done()
diff --git a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/source/gc_annotations.go
index b6a739d..8e42c66 100644
--- a/gopls/internal/lsp/source/gc_annotations.go
+++ b/gopls/internal/lsp/source/gc_annotations.go
@@ -13,12 +13,13 @@
"path/filepath"
"strings"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/internal/gocommand"
)
-func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, m *Metadata) (map[protocol.DocumentURI][]*Diagnostic, error) {
+func GCOptimizationDetails(ctx context.Context, snapshot *cache.Snapshot, m *Metadata) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
if len(m.CompiledGoFiles) == 0 {
return nil, nil
}
@@ -49,7 +50,7 @@
},
WorkingDir: pkgDir,
}
- _, err = snapshot.RunGoCommandDirect(ctx, Normal, inv)
+ _, err = snapshot.RunGoCommandDirect(ctx, cache.Normal, inv)
if err != nil {
return nil, err
}
@@ -57,7 +58,7 @@
if err != nil {
return nil, err
}
- reports := make(map[protocol.DocumentURI][]*Diagnostic)
+ reports := make(map[protocol.DocumentURI][]*cache.Diagnostic)
opts := snapshot.Options()
var parseError error
for _, fn := range files {
@@ -81,7 +82,7 @@
return reports, parseError
}
-func parseDetailsFile(filename string, options *settings.Options) (protocol.DocumentURI, []*Diagnostic, error) {
+func parseDetailsFile(filename string, options *settings.Options) (protocol.DocumentURI, []*cache.Diagnostic, error) {
buf, err := os.ReadFile(filename)
if err != nil {
return "", nil, err
@@ -89,7 +90,7 @@
var (
uri protocol.DocumentURI
i int
- diagnostics []*Diagnostic
+ diagnostics []*cache.Diagnostic
)
type metadata struct {
File string `json:"file,omitempty"`
@@ -135,12 +136,12 @@
Message: ri.Message,
})
}
- diagnostic := &Diagnostic{
+ diagnostic := &cache.Diagnostic{
URI: uri,
Range: zeroIndexedRange(d.Range),
Message: msg,
Severity: d.Severity,
- Source: OptimizationDetailsError, // d.Source is always "go compiler" as of 1.16, use our own
+ Source: cache.OptimizationDetailsError, // d.Source is always "go compiler" as of 1.16, use our own
Tags: d.Tags,
Related: related,
}
diff --git a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/highlight.go
index 19a68ba..4ec7fe7 100644
--- a/gopls/internal/lsp/source/highlight.go
+++ b/gopls/internal/lsp/source/highlight.go
@@ -13,12 +13,13 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/typesutil"
"golang.org/x/tools/internal/event"
)
-func Highlight(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Range, error) {
+func Highlight(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Range, error) {
ctx, done := event.Start(ctx, "source.Highlight")
defer done()
diff --git a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover.go
index c4908fa..3887824 100644
--- a/gopls/internal/lsp/source/hover.go
+++ b/gopls/internal/lsp/source/hover.go
@@ -26,6 +26,7 @@
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/gopls/internal/settings"
@@ -63,7 +64,7 @@
}
// Hover implements the "textDocument/hover" RPC for Go files.
-func Hover(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
+func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
ctx, done := event.Start(ctx, "source.Hover")
defer done()
@@ -90,7 +91,7 @@
// hover computes hover information at the given position. If we do not support
// hovering at the position, it returns _, nil, nil: an error is only returned
// if the position is valid but we fail to compute hover information.
-func hover(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position) (protocol.Range, *HoverJSON, error) {
+func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) (protocol.Range, *HoverJSON, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return protocol.Range{}, nil, err
@@ -366,7 +367,7 @@
// hoverBuiltin computes hover information when hovering over a builtin
// identifier.
-func hoverBuiltin(ctx context.Context, snapshot Snapshot, obj types.Object) (*HoverJSON, error) {
+func hoverBuiltin(ctx context.Context, snapshot *cache.Snapshot, obj types.Object) (*HoverJSON, error) {
// TODO(rfindley): link to the correct version of Go documentation.
builtin, err := snapshot.BuiltinFile(ctx)
if err != nil {
@@ -436,7 +437,7 @@
// imp in the file pgf of pkg.
//
// If we do not have metadata for the hovered import, it returns _
-func hoverImport(ctx context.Context, snapshot Snapshot, pkg Package, pgf *ParsedGoFile, imp *ast.ImportSpec) (protocol.Range, *HoverJSON, error) {
+func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *ParsedGoFile, imp *ast.ImportSpec) (protocol.Range, *HoverJSON, error) {
rng, err := pgf.NodeRange(imp.Path)
if err != nil {
return protocol.Range{}, nil, err
@@ -487,7 +488,7 @@
// hoverPackageName computes hover information for the package name of the file
// pgf in pkg.
-func hoverPackageName(pkg Package, pgf *ParsedGoFile) (protocol.Range, *HoverJSON, error) {
+func hoverPackageName(pkg *cache.Package, pgf *ParsedGoFile) (protocol.Range, *HoverJSON, error) {
var comment *ast.CommentGroup
for _, pgf := range pkg.CompiledGoFiles() {
if pgf.File.Doc != nil {
@@ -761,7 +762,7 @@
// fset provides file/line information).
//
// TODO(rfindley): there appears to be zero(!) tests for this functionality.
-func HoverDocForObject(ctx context.Context, snapshot Snapshot, fset *token.FileSet, obj types.Object) (*ast.CommentGroup, error) {
+func HoverDocForObject(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, obj types.Object) (*ast.CommentGroup, error) {
if _, isTypeName := obj.(*types.TypeName); isTypeName {
if _, isTypeParam := obj.Type().(*typeparams.TypeParam); isTypeParam {
return nil, nil
@@ -818,7 +819,7 @@
//
// It returns the resulting ParsedGoFile as well as new pos contained in the
// parsed file.
-func parseFull(ctx context.Context, snapshot Snapshot, fset *token.FileSet, pos token.Pos) (*ParsedGoFile, token.Pos, error) {
+func parseFull(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, pos token.Pos) (*ParsedGoFile, token.Pos, error) {
f := fset.File(pos)
if f == nil {
return nil, 0, bug.Errorf("internal error: no file for position %d", pos)
@@ -899,7 +900,7 @@
if !options.LinksInHover || options.LinkTarget == "" || h.LinkPath == "" {
return ""
}
- plainLink := BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor)
+ plainLink := cache.BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor)
switch options.PreferredContentFormat {
case protocol.Markdown:
return fmt.Sprintf("[`%s` on %s](%s)", h.SymbolName, options.LinkTarget, plainLink)
@@ -910,15 +911,6 @@
}
}
-// BuildLink constructs a URL with the given target, path, and anchor.
-func BuildLink(target, path, anchor string) string {
- link := fmt.Sprintf("https://%s/%s", target, path)
- if anchor == "" {
- return link
- }
- return link + "#" + anchor
-}
-
func formatDoc(h *HoverJSON, options *settings.Options) string {
var doc string
switch options.HoverKind {
diff --git a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/source/implementation.go
index e6c9be5..a5aae08 100644
--- a/gopls/internal/lsp/source/implementation.go
+++ b/gopls/internal/lsp/source/implementation.go
@@ -19,6 +19,8 @@
"golang.org/x/sync/errgroup"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
+ "golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/cache/methodsets"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -46,7 +48,7 @@
//
// If the position denotes a method, the computation is applied to its
// receiver type and then its corresponding methods are returned.
-func Implementation(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position) ([]protocol.Location, error) {
+func Implementation(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position) ([]protocol.Location, error) {
ctx, done := event.Start(ctx, "source.Implementation")
defer done()
@@ -70,13 +72,13 @@
return locs, nil
}
-func implementations(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.Location, error) {
+func implementations(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.Location, error) {
obj, pkg, err := implementsObj(ctx, snapshot, fh.URI(), pp)
if err != nil {
return nil, err
}
- var localPkgs []Package
+ var localPkgs []*cache.Package
if obj.Pos().IsValid() { // no local package for error or error.Error
declPosn := safetoken.StartPosition(pkg.FileSet(), obj.Pos())
// Type-check the declaring package (incl. variants) for use
@@ -88,7 +90,7 @@
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&declMetas)
+ metadata.RemoveIntermediateTestVariants(&declMetas)
if len(declMetas) == 0 {
return nil, fmt.Errorf("no packages for file %s", declURI)
}
@@ -141,7 +143,7 @@
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&globalMetas)
+ metadata.RemoveIntermediateTestVariants(&globalMetas)
globalIDs := make([]PackageID, 0, len(globalMetas))
var pkgPath PackagePath
@@ -209,7 +211,7 @@
// offsetToLocation converts an offset-based position to a protocol.Location,
// which requires reading the file.
-func offsetToLocation(ctx context.Context, snapshot Snapshot, filename string, start, end int) (protocol.Location, error) {
+func offsetToLocation(ctx context.Context, snapshot *cache.Snapshot, filename string, start, end int) (protocol.Location, error) {
uri := protocol.URIFromPath(filename)
fh, err := snapshot.ReadFile(ctx, uri)
if err != nil {
@@ -228,7 +230,7 @@
//
// The returned Package is the narrowest package containing ppos, which is the
// package using the resulting obj but not necessarily the declaring package.
-func implementsObj(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI, ppos protocol.Position) (types.Object, Package, error) {
+func implementsObj(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, ppos protocol.Position) (types.Object, *cache.Package, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri)
if err != nil {
return nil, nil, err
@@ -294,7 +296,7 @@
// function's results may include type declarations that are local to
// a function body. The global search index excludes such types
// because reliably naming such types is hard.)
-func localImplementations(ctx context.Context, snapshot Snapshot, pkg Package, queryType types.Type, methodID string) ([]protocol.Location, error) {
+func localImplementations(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, queryType types.Type, methodID string) ([]protocol.Location, error) {
queryType = methodsets.EnsurePointer(queryType)
// Scan through all type declarations in the syntax.
@@ -384,7 +386,7 @@
var errorInterfaceType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
// errorLocation returns the location of the 'error' type in builtin.go.
-func errorLocation(ctx context.Context, snapshot Snapshot) (protocol.Location, error) {
+func errorLocation(ctx context.Context, snapshot *cache.Snapshot) (protocol.Location, error) {
pgf, err := snapshot.BuiltinFile(ctx)
if err != nil {
return protocol.Location{}, err
diff --git a/gopls/internal/lsp/source/inlay_hint.go b/gopls/internal/lsp/source/inlay_hint.go
index d82b8e1..ae63277 100644
--- a/gopls/internal/lsp/source/inlay_hint.go
+++ b/gopls/internal/lsp/source/inlay_hint.go
@@ -14,6 +14,7 @@
"strings"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/typeparams"
@@ -79,7 +80,7 @@
},
}
-func InlayHint(ctx context.Context, snapshot Snapshot, fh file.Handle, pRng protocol.Range) ([]protocol.InlayHint, error) {
+func InlayHint(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pRng protocol.Range) ([]protocol.InlayHint, error) {
ctx, done := event.Start(ctx, "source.InlayHint")
defer done()
diff --git a/gopls/internal/lsp/source/inline.go b/gopls/internal/lsp/source/inline.go
index 5619b23..48f417e 100644
--- a/gopls/internal/lsp/source/inline.go
+++ b/gopls/internal/lsp/source/inline.go
@@ -18,6 +18,7 @@
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/internal/diff"
@@ -27,7 +28,7 @@
// EnclosingStaticCall returns the innermost function call enclosing
// the selected range, along with the callee.
-func EnclosingStaticCall(pkg Package, pgf *ParsedGoFile, rng protocol.Range) (*ast.CallExpr, *types.Func, error) {
+func EnclosingStaticCall(pkg *cache.Package, pgf *ParsedGoFile, rng protocol.Range) (*ast.CallExpr, *types.Func, error) {
start, end, err := pgf.RangePos(rng)
if err != nil {
return nil, nil, err
@@ -58,7 +59,7 @@
return call, fn, nil
}
-func inlineCall(ctx context.Context, snapshot Snapshot, fh file.Handle, rng protocol.Range) (_ []protocol.TextDocumentEdit, err error) {
+func inlineCall(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) (_ []protocol.TextDocumentEdit, err error) {
// Find enclosing static call.
callerPkg, callerPGF, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
@@ -93,7 +94,7 @@
// but that is frequently not the case within gopls.
// Until we are able to harden the inliner,
// report panics as errors to avoid crashing the server.
- bad := func(p Package) bool { return len(p.GetParseErrors())+len(p.GetTypeErrors()) > 0 }
+ bad := func(p *cache.Package) bool { return len(p.GetParseErrors())+len(p.GetTypeErrors()) > 0 }
if bad(calleePkg) || bad(callerPkg) {
defer func() {
if x := recover(); x != nil {
diff --git a/gopls/internal/lsp/source/inline_all.go b/gopls/internal/lsp/source/inline_all.go
index a15a279..48e99d8 100644
--- a/gopls/internal/lsp/source/inline_all.go
+++ b/gopls/internal/lsp/source/inline_all.go
@@ -14,6 +14,7 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/gopls/internal/bug"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/refactor/inline"
)
@@ -42,7 +43,7 @@
//
// The code below notes where are assumptions are made that only hold true in
// the case of parameter removal (annotated with 'Assumption:')
-func inlineAllCalls(ctx context.Context, logf func(string, ...any), snapshot Snapshot, pkg Package, pgf *ParsedGoFile, origDecl *ast.FuncDecl, callee *inline.Callee, post func([]byte) []byte) (map[protocol.DocumentURI][]byte, error) {
+func inlineAllCalls(ctx context.Context, logf func(string, ...any), snapshot *cache.Snapshot, pkg *cache.Package, pgf *ParsedGoFile, origDecl *ast.FuncDecl, callee *inline.Callee, post func([]byte) []byte) (map[protocol.DocumentURI][]byte, error) {
// Collect references.
var refs []protocol.Location
{
@@ -65,7 +66,7 @@
// parallel and to reduce peak memory for this operation.
var (
pkgForRef = make(map[protocol.Location]PackageID)
- pkgs = make(map[PackageID]Package)
+ pkgs = make(map[PackageID]*cache.Package)
)
{
needPkgs := make(map[PackageID]struct{})
@@ -98,7 +99,7 @@
// declaration, we must re-type check.
type fileCalls struct {
- pkg Package
+ pkg *cache.Package
pgf *ParsedGoFile
calls []*ast.CallExpr
}
diff --git a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/source/known_packages.go
index 8f41746..80de962 100644
--- a/gopls/internal/lsp/source/known_packages.go
+++ b/gopls/internal/lsp/source/known_packages.go
@@ -14,6 +14,7 @@
"time"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/imports"
)
@@ -24,7 +25,7 @@
// all dot-free paths (standard packages) appear before dotful ones.
//
// It is part of the gopls.list_known_packages command.
-func KnownPackagePaths(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]PackagePath, error) {
+func KnownPackagePaths(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]PackagePath, error) {
// This algorithm is expressed in terms of Metadata, not Packages,
// so it doesn't cause or wait for type checking.
@@ -74,7 +75,7 @@
continue
}
// make sure internal packages are importable by the file
- if !IsValidImport(current.PkgPath, knownPkg.PkgPath) {
+ if !cache.IsValidImport(current.PkgPath, knownPkg.PkgPath) {
continue
}
// naive check on cyclical imports
diff --git a/gopls/internal/lsp/source/linkname.go b/gopls/internal/lsp/source/linkname.go
index 1355461..ce62707 100644
--- a/gopls/internal/lsp/source/linkname.go
+++ b/gopls/internal/lsp/source/linkname.go
@@ -11,6 +11,8 @@
"go/token"
"strings"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
+ "golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
)
@@ -22,7 +24,7 @@
// LinknameDefinition finds the definition of the linkname directive in m at pos.
// If there is no linkname directive at pos, returns ErrNoLinkname.
-func LinknameDefinition(ctx context.Context, snapshot Snapshot, m *protocol.Mapper, from protocol.Position) ([]protocol.Location, error) {
+func LinknameDefinition(ctx context.Context, snapshot *cache.Snapshot, m *protocol.Mapper, from protocol.Position) ([]protocol.Location, error) {
pkgPath, name, _ := parseLinkname(m, from)
if pkgPath == "" {
return nil, ErrNoLinkname
@@ -100,7 +102,7 @@
// findLinkname searches dependencies of packages containing fh for an object
// with linker name matching the given package path and name.
-func findLinkname(ctx context.Context, snapshot Snapshot, pkgPath PackagePath, name string) (Package, *ParsedGoFile, token.Pos, error) {
+func findLinkname(ctx context.Context, snapshot *cache.Snapshot, pkgPath PackagePath, name string) (*cache.Package, *ParsedGoFile, token.Pos, error) {
// Typically the linkname refers to a forward dependency
// or a reverse dependency, but in general it may refer
// to any package that is linked with this one.
@@ -109,7 +111,7 @@
if err != nil {
return nil, nil, token.NoPos, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
for _, meta := range metas {
if meta.PkgPath == pkgPath {
pkgMeta = meta
diff --git a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/references.go
index baf2a5c..a106fff 100644
--- a/gopls/internal/lsp/source/references.go
+++ b/gopls/internal/lsp/source/references.go
@@ -27,6 +27,7 @@
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/methodsets"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -36,7 +37,7 @@
// References returns a list of all references (sorted with
// definitions before uses) to the object denoted by the identifier at
// the given file/position, searching the entire workspace.
-func References(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position, includeDeclaration bool) ([]protocol.Location, error) {
+func References(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position, includeDeclaration bool) ([]protocol.Location, error) {
references, err := references(ctx, snapshot, fh, pp, includeDeclaration)
if err != nil {
return nil, err
@@ -59,7 +60,7 @@
// references returns a list of all references (sorted with
// definitions before uses) to the object denoted by the identifier at
// the given file/position, searching the entire workspace.
-func references(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position, includeDeclaration bool) ([]reference, error) {
+func references(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, includeDeclaration bool) ([]reference, error) {
ctx, done := event.Start(ctx, "source.references")
defer done()
@@ -106,7 +107,7 @@
// declaration of the specified name and uri by searching among the
// import declarations of all packages that directly import the target
// package.
-func packageReferences(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) ([]reference, error) {
+func packageReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) ([]reference, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, err
@@ -206,7 +207,7 @@
}
// ordinaryReferences computes references for all ordinary objects (not package declarations).
-func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI, pp protocol.Position) ([]reference, error) {
+func ordinaryReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, pp protocol.Position) ([]reference, error) {
// Strategy: use the reference information computed by the
// type checker to find the declaration. First type-check this
// package to find the declaration, then type check the
@@ -504,7 +505,7 @@
// The scope is expanded by a sequence of calls (not concurrent) to addRdeps.
//
// recv is the method's effective receiver type, for method-set computations.
-func expandMethodSearch(ctx context.Context, snapshot Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
+func expandMethodSearch(ctx context.Context, snapshot *cache.Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
// Compute the method-set fingerprint used as a key to the global search.
key, hasMethods := methodsets.KeyOf(recv)
if !hasMethods {
@@ -561,7 +562,7 @@
// localReferences traverses syntax and reports each reference to one
// of the target objects, or (if correspond is set) an object that
// corresponds to one of them via interface satisfaction.
-func localReferences(pkg Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error {
+func localReferences(pkg *cache.Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error {
// If we're searching for references to a method optionally
// broaden the search to include references to corresponding
// methods of mutually assignable receiver types.
diff --git a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go
index a98a722..17c0df9 100644
--- a/gopls/internal/lsp/source/rename.go
+++ b/gopls/internal/lsp/source/rename.go
@@ -61,6 +61,7 @@
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -74,7 +75,7 @@
// an object (or several coupled objects) within a single type-checked
// syntax package.
type renamer struct {
- pkg Package // the syntax package in which the renaming is applied
+ pkg *cache.Package // the syntax package in which the renaming is applied
objsToUpdate map[types.Object]bool // records progress of calls to check
hadConflicts bool
conflicts []string
@@ -96,7 +97,7 @@
// The returned usererr is intended to be displayed to the user to explain why
// the prepare fails. Probably we could eliminate the redundancy in returning
// two errors, but for now this is done defensively.
-func PrepareRename(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position) (_ *PrepareItem, usererr, err error) {
+func PrepareRename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position) (_ *PrepareItem, usererr, err error) {
ctx, done := event.Start(ctx, "source.PrepareRename")
defer done()
@@ -150,7 +151,7 @@
}, nil, nil
}
-func prepareRenamePackageName(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile) (*PrepareItem, error) {
+func prepareRenamePackageName(ctx context.Context, snapshot *cache.Snapshot, pgf *ParsedGoFile) (*PrepareItem, error) {
// Does the client support file renaming?
fileRenameSupported := false
for _, op := range snapshot.Options().SupportedResourceOperations {
@@ -214,7 +215,7 @@
// Rename returns a map of TextEdits for each file modified when renaming a
// given identifier within a package and a boolean value of true for renaming
// package and false otherwise.
-func Rename(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]protocol.TextEdit, bool, error) {
+func Rename(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]protocol.TextEdit, bool, error) {
ctx, done := event.Start(ctx, "source.Rename")
defer done()
@@ -288,7 +289,7 @@
}
// renameOrdinary renames an ordinary (non-package) name throughout the workspace.
-func renameOrdinary(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]diff.Edit, error) {
+func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, newName string) (map[protocol.DocumentURI][]diff.Edit, error) {
// Type-check the referring package and locate the object(s).
//
// Unlike NarrowestPackageForFile, this operation prefers the
@@ -297,13 +298,13 @@
// 'references' doesn't also want the widest variant: it
// computes the union across all variants.)
var targets map[types.Object]ast.Node
- var pkg Package
+ var pkg *cache.Package
{
metas, err := snapshot.MetadataForFile(ctx, f.URI())
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
if len(metas) == 0 {
return nil, fmt.Errorf("no package metadata for file %s", f.URI())
}
@@ -477,7 +478,7 @@
// (This neglects obscure edge cases where a _test.go file changes the
// selectors used only in an ITV, but life is short. Also sin must be
// punished.)
-func typeCheckReverseDependencies(ctx context.Context, snapshot Snapshot, declURI protocol.DocumentURI, transitive bool) ([]Package, error) {
+func typeCheckReverseDependencies(ctx context.Context, snapshot *cache.Snapshot, declURI protocol.DocumentURI, transitive bool) ([]*cache.Package, error) {
variants, err := snapshot.MetadataForFile(ctx, declURI)
if err != nil {
return nil, err
@@ -525,7 +526,7 @@
// within the specified packages, along with any other objects that
// must be renamed as a consequence. The slice of packages must be
// topologically ordered.
-func renameExported(pkgs []Package, declPkgPath PackagePath, declObjPath objectpath.Path, newName string) (map[protocol.DocumentURI][]diff.Edit, error) {
+func renameExported(pkgs []*cache.Package, declPkgPath PackagePath, declObjPath objectpath.Path, newName string) (map[protocol.DocumentURI][]diff.Edit, error) {
// A target is a name for an object that is stable across types.Packages.
type target struct {
@@ -614,7 +615,7 @@
}
// renamePackageName renames package declarations, imports, and go.mod files.
-func renamePackageName(ctx context.Context, s Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) {
+func renamePackageName(ctx context.Context, s *cache.Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) {
// Rename the package decl and all imports.
renamingEdits, err := renamePackage(ctx, s, f, newName)
if err != nil {
@@ -717,7 +718,7 @@
// It updates package clauses and import paths for the renamed package as well
// as any other packages affected by the directory renaming among all packages
// known to the snapshot.
-func renamePackage(ctx context.Context, s Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) {
+func renamePackage(ctx context.Context, s *cache.Snapshot, f file.Handle, newName PackageName) (map[protocol.DocumentURI][]diff.Edit, error) {
if strings.HasSuffix(string(newName), "_test") {
return nil, fmt.Errorf("cannot rename to _test package")
}
@@ -804,7 +805,7 @@
// the package described by the given metadata, to newName.
//
// Edits are written into the edits map.
-func renamePackageClause(ctx context.Context, m *Metadata, snapshot Snapshot, newName PackageName, edits map[protocol.DocumentURI][]diff.Edit) error {
+func renamePackageClause(ctx context.Context, m *Metadata, snapshot *cache.Snapshot, newName PackageName, edits map[protocol.DocumentURI][]diff.Edit) error {
// Rename internal references to the package in the renaming package.
for _, uri := range m.CompiledGoFiles {
fh, err := snapshot.ReadFile(ctx, uri)
@@ -834,7 +835,7 @@
// newPath and name newName.
//
// Edits are written into the edits map.
-func renameImports(ctx context.Context, snapshot Snapshot, m *Metadata, newPath ImportPath, newName PackageName, allEdits map[protocol.DocumentURI][]diff.Edit) error {
+func renameImports(ctx context.Context, snapshot *cache.Snapshot, m *Metadata, newPath ImportPath, newName PackageName, allEdits map[protocol.DocumentURI][]diff.Edit) error {
rdeps, err := snapshot.ReverseDependencies(ctx, m.ID, false) // find direct importers
if err != nil {
return err
@@ -980,7 +981,7 @@
// consequence of the requested renamings.
//
// It returns an error if the renaming would cause a conflict.
-func renameObjects(newName string, pkg Package, targets ...types.Object) (map[protocol.DocumentURI][]diff.Edit, map[types.Object]bool, error) {
+func renameObjects(newName string, pkg *cache.Package, targets ...types.Object) (map[protocol.DocumentURI][]diff.Edit, map[types.Object]bool, error) {
r := renamer{
pkg: pkg,
objsToUpdate: make(map[types.Object]bool),
@@ -1222,7 +1223,7 @@
// whether the position ppos lies within it.
//
// Note: also used by references.
-func parsePackageNameDecl(ctx context.Context, snapshot Snapshot, fh file.Handle, ppos protocol.Position) (*ParsedGoFile, bool, error) {
+func parsePackageNameDecl(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, ppos protocol.Position) (*ParsedGoFile, bool, error) {
pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader)
if err != nil {
return nil, false, err
@@ -1234,7 +1235,7 @@
}
// enclosingFile returns the CompiledGoFile of pkg that contains the specified position.
-func enclosingFile(pkg Package, pos token.Pos) (*ParsedGoFile, bool) {
+func enclosingFile(pkg *cache.Package, pos token.Pos) (*ParsedGoFile, bool) {
for _, pgf := range pkg.CompiledGoFiles() {
if pgf.File.Pos() <= pos && pos <= pgf.File.End() {
return pgf, true
diff --git a/gopls/internal/lsp/source/rename_check.go b/gopls/internal/lsp/source/rename_check.go
index 5334174..6261752 100644
--- a/gopls/internal/lsp/source/rename_check.go
+++ b/gopls/internal/lsp/source/rename_check.go
@@ -43,6 +43,7 @@
"unicode"
"golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/refactor/satisfy"
)
@@ -298,7 +299,7 @@
// pkg that is a reference to obj in lexical scope. block is the
// lexical block enclosing the reference. If fn returns false the
// iteration is terminated and findLexicalRefs returns false.
-func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
+func forEachLexicalRef(pkg *cache.Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
ok := true
var stack []ast.Node
diff --git a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/source/signature_help.go
index 2c6745c..c36c56e 100644
--- a/gopls/internal/lsp/source/signature_help.go
+++ b/gopls/internal/lsp/source/signature_help.go
@@ -15,12 +15,13 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/internal/event"
)
-func SignatureHelp(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) (*protocol.SignatureInformation, int, error) {
+func SignatureHelp(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.SignatureInformation, int, error) {
ctx, done := event.Start(ctx, "source.SignatureHelp")
defer done()
@@ -140,7 +141,7 @@
}, activeParam, nil
}
-func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
+func builtinSignature(ctx context.Context, snapshot *cache.Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
sig, err := NewBuiltinSignature(ctx, snapshot, name)
if err != nil {
return nil, 0, err
diff --git a/gopls/internal/lsp/source/snapshot.go b/gopls/internal/lsp/source/snapshot.go
index 0361900..ae740f4 100644
--- a/gopls/internal/lsp/source/snapshot.go
+++ b/gopls/internal/lsp/source/snapshot.go
@@ -5,36 +5,17 @@
package source
import (
- "bytes"
"context"
- "encoding/json"
"fmt"
- "go/ast"
- "go/parser"
- "go/scanner"
- "go/token"
- "go/types"
- "golang.org/x/mod/modfile"
- "golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
- "golang.org/x/tools/gopls/internal/lsp/cache/methodsets"
"golang.org/x/tools/gopls/internal/lsp/cache/parsego"
- "golang.org/x/tools/gopls/internal/lsp/progress"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/settings"
- "golang.org/x/tools/internal/gocommand"
- "golang.org/x/tools/internal/imports"
)
-// 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 {
// FileKind returns the type of a file.
@@ -186,26 +167,23 @@
// Unlike [GoVersion], this encodes the minor version and commit hash information.
GoVersionString() string
}
+*/
// NarrowestMetadataForFile returns metadata for the narrowest package
// (the one with the fewest files) that encloses the specified file.
// The result may be a test variant, but never an intermediate test variant.
-func NarrowestMetadataForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (*Metadata, error) {
+func NarrowestMetadataForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*Metadata, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
if len(metas) == 0 {
return nil, fmt.Errorf("no package metadata for file %s", uri)
}
return metas[0], nil
}
-type XrefIndex interface {
- Lookup(targets map[PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location)
-}
-
// NarrowestPackageForFile is a convenience function that selects the narrowest
// non-ITV package to which this file belongs, type-checks it in the requested
// mode (full or workspace), and returns it, along with the parse tree of that
@@ -221,7 +199,7 @@
//
// Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
// tree, or snapshot.MetadataForFile if you only need metadata.
-func NarrowestPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (Package, *ParsedGoFile, error) {
+func NarrowestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
return selectPackageForFile(ctx, snapshot, uri, func(metas []*Metadata) *Metadata { return metas[0] })
}
@@ -239,16 +217,16 @@
//
// Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
// tree, or snapshot.MetadataForFile if you only need metadata.
-func WidestPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (Package, *ParsedGoFile, error) {
+func WidestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
return selectPackageForFile(ctx, snapshot, uri, func(metas []*Metadata) *Metadata { return metas[len(metas)-1] })
}
-func selectPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI, selector func([]*Metadata) *Metadata) (Package, *ParsedGoFile, error) {
+func selectPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, selector func([]*Metadata) *Metadata) (*cache.Package, *ParsedGoFile, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, nil, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
if len(metas) == 0 {
return nil, nil, fmt.Errorf("no package metadata for file %s", uri)
}
@@ -265,34 +243,6 @@
return pkg, pgf, err
}
-// 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
-}
-
// A FileSource maps URIs to FileHandles.
type FileSource interface {
// ReadFile returns the FileHandle for a given URI, either by
@@ -305,43 +255,6 @@
type ParsedGoFile = parsego.File
-// A ParsedModule contains the results of parsing a go.mod file.
-type ParsedModule struct {
- URI protocol.DocumentURI
- File *modfile.File
- Mapper *protocol.Mapper
- ParseErrors []*Diagnostic
-}
-
-// A ParsedWorkFile contains the results of parsing a go.work file.
-type ParsedWorkFile struct {
- URI protocol.DocumentURI
- 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
-}
-
-// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array.
-// We use a pointer to a slice make it impossible to forget to use the result.
-func RemoveIntermediateTestVariants(pmetas *[]*Metadata) {
- metas := *pmetas
- res := metas[:0]
- for _, m := range metas {
- if !m.IsIntermediateTestVariant() {
- res = append(res, m)
- }
- }
- *pmetas = res
-}
-
const (
ParseHeader = parsego.ParseHeader
ParseFull = parsego.ParseFull
@@ -355,99 +268,4 @@
Metadata = metadata.Metadata
)
-// 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
- CompiledGoFiles() []*ParsedGoFile // (borrowed)
- File(uri protocol.DocumentURI) (*ParsedGoFile, error)
- GetSyntax() []*ast.File // (borrowed)
- GetParseErrors() []scanner.ErrorList
-
- // Results of type checking:
- GetTypes() *types.Package
- GetTypeErrors() []types.Error
- GetTypesInfo() *types.Info
- DependencyTypes(PackagePath) *types.Package // nil for indirect dependency of no consequence
- DiagnosticsForFile(ctx context.Context, uri protocol.DocumentURI) ([]*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 {
- // TODO(adonovan): should be a protocol.URI, for symmetry.
- URI protocol.DocumentURI // of diagnosed file (not diagnostic documentation)
- 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 historically didn't leave the server.
- //
- // Update(2023-05): version 3.16 of the LSP spec included support for the
- // Diagnostic.data field, which holds arbitrary data preserved in the
- // diagnostic for codeAction requests. This field allows bundling additional
- // information for quick-fixes, and gopls can (and should) use this
- // information to avoid re-evaluating diagnostics in code-action handlers.
- //
- // In order to stage this transition incrementally, the 'BundledFixes' field
- // may store a 'bundled' (=json-serialized) form of the associated
- // SuggestedFixes. Not all diagnostics have their fixes bundled.
- BundledFixes *json.RawMessage
- 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"
- ConsistencyInfo DiagnosticSource = "consistency"
-)
diff --git a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.go
index d93712c..58119d2 100644
--- a/gopls/internal/lsp/source/stub.go
+++ b/gopls/internal/lsp/source/stub.go
@@ -21,6 +21,7 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/internal/diff"
@@ -31,7 +32,7 @@
// stubSuggestedFixFunc returns a suggested fix to declare the missing
// methods of the concrete type that is assigned to an interface type
// at the cursor position.
-func stubSuggestedFixFunc(ctx context.Context, snapshot Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
+func stubSuggestedFixFunc(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, fmt.Errorf("GetTypedFile: %w", err)
@@ -53,7 +54,7 @@
}
// stub returns a suggested fix to declare the missing methods of si.Concrete.
-func stub(ctx context.Context, snapshot Snapshot, si *stubmethods.StubInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
+func stub(ctx context.Context, snapshot *cache.Snapshot, si *stubmethods.StubInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
// A function-local type cannot be stubbed
// since there's nowhere to put the methods.
conc := si.Concrete.Obj()
diff --git a/gopls/internal/lsp/source/symbols.go b/gopls/internal/lsp/source/symbols.go
index 8369043..6768118 100644
--- a/gopls/internal/lsp/source/symbols.go
+++ b/gopls/internal/lsp/source/symbols.go
@@ -12,11 +12,12 @@
"go/types"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)
-func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]protocol.DocumentSymbol, error) {
+func DocumentSymbols(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.DocumentSymbol, error) {
ctx, done := event.Start(ctx, "source.DocumentSymbols")
defer done()
diff --git a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/source/type_definition.go
index 2a09c59..19a0a89 100644
--- a/gopls/internal/lsp/source/type_definition.go
+++ b/gopls/internal/lsp/source/type_definition.go
@@ -11,12 +11,13 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)
// TypeDefinition handles the textDocument/typeDefinition request for Go files.
-func TypeDefinition(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
+func TypeDefinition(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
ctx, done := event.Start(ctx, "source.TypeDefinition")
defer done()
diff --git a/gopls/internal/lsp/source/types_format.go b/gopls/internal/lsp/source/types_format.go
index b58281d..716f347 100644
--- a/gopls/internal/lsp/source/types_format.go
+++ b/gopls/internal/lsp/source/types_format.go
@@ -16,6 +16,7 @@
"strings"
"golang.org/x/tools/gopls/internal/bug"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/internal/event"
@@ -88,7 +89,7 @@
// NewBuiltinSignature returns signature for the builtin object with a given
// name, if a builtin object with the name exists.
-func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) {
+func NewBuiltinSignature(ctx context.Context, s *cache.Snapshot, name string) (*signature, error) {
builtin, err := s.BuiltinFile(ctx)
if err != nil {
return nil, err
@@ -198,7 +199,7 @@
}
// NewSignature returns formatted signature for a types.Signature struct.
-func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier, mq MetadataQualifier) (*signature, error) {
+func NewSignature(ctx context.Context, s *cache.Snapshot, pkg *cache.Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier, mq MetadataQualifier) (*signature, error) {
var tparams []string
tpList := typeparams.ForSignature(sig)
for i := 0; i < tpList.Len(); i++ {
@@ -268,7 +269,7 @@
//
// TODO(rfindley): this function could return the actual name used in syntax,
// for better parameter names.
-func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, obj *types.Var, qf types.Qualifier, mq MetadataQualifier) (string, error) {
+func FormatVarType(ctx context.Context, snapshot *cache.Snapshot, srcpkg *cache.Package, obj *types.Var, qf types.Qualifier, mq MetadataQualifier) (string, error) {
// TODO(rfindley): This looks wrong. The previous comment said:
// "If the given expr refers to a type parameter, then use the
// object's Type instead of the type parameter declaration. This helps
diff --git a/gopls/internal/lsp/source/util.go b/gopls/internal/lsp/source/util.go
index 06bbe59..06f1c0d 100644
--- a/gopls/internal/lsp/source/util.go
+++ b/gopls/internal/lsp/source/util.go
@@ -16,6 +16,7 @@
"golang.org/x/tools/gopls/internal/astutil"
"golang.org/x/tools/gopls/internal/bug"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -30,7 +31,7 @@
//
// TODO(adonovan): opt: this function does too much.
// Move snapshot.ReadFile into the caller (most of which have already done it).
-func IsGenerated(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) bool {
+func IsGenerated(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) bool {
fh, err := snapshot.ReadFile(ctx, uri)
if err != nil {
return false
@@ -138,13 +139,13 @@
}
}
-func SortDiagnostics(d []*Diagnostic) {
+func SortDiagnostics(d []*cache.Diagnostic) {
sort.Slice(d, func(i int, j int) bool {
return CompareDiagnostic(d[i], d[j]) < 0
})
}
-func CompareDiagnostic(a, b *Diagnostic) int {
+func CompareDiagnostic(a, b *cache.Diagnostic) int {
if r := protocol.CompareRange(a.Range, b.Range); r != 0 {
return r
}
@@ -415,23 +416,6 @@
return true
}
-// IsValidImport returns whether importPkgPath is importable
-// by pkgPath
-func IsValidImport(pkgPath, importPkgPath PackagePath) bool {
- i := strings.LastIndex(string(importPkgPath), "/internal/")
- if i == -1 {
- return true
- }
- // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to
- // operate on package IDs, not package paths.
- if metadata.IsCommandLineArguments(PackageID(pkgPath)) {
- return true
- }
- // TODO(rfindley): this is wrong. mod.testx/p should not be able to
- // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q
- return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
-}
-
// embeddedIdent returns the type name identifier for an embedding x, if x in a
// valid embedding. Otherwise, it returns nil.
//
diff --git a/gopls/internal/lsp/source/workspace_symbol.go b/gopls/internal/lsp/source/workspace_symbol.go
index 0267da1..117490c 100644
--- a/gopls/internal/lsp/source/workspace_symbol.go
+++ b/gopls/internal/lsp/source/workspace_symbol.go
@@ -7,14 +7,13 @@
import (
"context"
"fmt"
- "path"
"path/filepath"
- "regexp"
"runtime"
"sort"
"strings"
"unicode"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/settings"
@@ -22,15 +21,6 @@
"golang.org/x/tools/internal/fuzzy"
)
-// Symbol holds a precomputed symbol value. Note: we avoid using the
-// protocol.SymbolInformation struct here in order to reduce the size of each
-// symbol.
-type Symbol struct {
- Name string
- Kind protocol.SymbolKind
- Range protocol.Range
-}
-
// maxSymbols defines the maximum number of symbol results that should ever be
// sent in response to a client.
const maxSymbols = 100
@@ -52,7 +42,7 @@
// with a different configured SymbolMatcher per View. Therefore we assume that
// Session level configuration will define the SymbolMatcher to be used for the
// WorkspaceSymbols method.
-func WorkspaceSymbols(ctx context.Context, matcher settings.SymbolMatcher, style settings.SymbolStyle, snapshots []Snapshot, query string) ([]protocol.SymbolInformation, error) {
+func WorkspaceSymbols(ctx context.Context, matcher settings.SymbolMatcher, style settings.SymbolStyle, snapshots []*cache.Snapshot, query string) ([]protocol.SymbolInformation, error) {
ctx, done := event.Start(ctx, "source.WorkspaceSymbols")
defer done()
if query == "" {
@@ -297,7 +287,7 @@
// of zero indicates no match.
// - A symbolizer determines how we extract the symbol for an object. This
// enables the 'symbolStyle' configuration option.
-func collectSymbols(ctx context.Context, snapshots []Snapshot, matcherType settings.SymbolMatcher, symbolizer symbolizer, query string) ([]protocol.SymbolInformation, error) {
+func collectSymbols(ctx context.Context, snapshots []*cache.Snapshot, matcherType settings.SymbolMatcher, symbolizer symbolizer, query string) ([]protocol.SymbolInformation, error) {
// Extract symbols from all files.
var work []symbolFile
var roots []string
@@ -310,7 +300,7 @@
roots = append(roots, strings.TrimRight(string(folderURI), "/"))
filters := snapshot.Options().DirectoryFilters
- filterer := NewFilterer(filters)
+ filterer := cache.NewFilterer(filters)
folder := filepath.ToSlash(folderURI.Path())
workspaceOnly := true
@@ -370,82 +360,11 @@
return unified.results(), nil
}
-type Filterer struct {
- // Whether a filter is excluded depends on the operator (first char of the raw filter).
- // Slices filters and excluded then should have the same length.
- filters []*regexp.Regexp
- excluded []bool
-}
-
-// NewFilterer computes regular expression form of all raw filters
-func NewFilterer(rawFilters []string) *Filterer {
- var f Filterer
- for _, filter := range rawFilters {
- filter = path.Clean(filepath.ToSlash(filter))
- // TODO(dungtuanle): fix: validate [+-] prefix.
- op, prefix := filter[0], filter[1:]
- // convertFilterToRegexp adds "/" at the end of prefix to handle cases where a filter is a prefix of another filter.
- // For example, it prevents [+foobar, -foo] from excluding "foobar".
- f.filters = append(f.filters, convertFilterToRegexp(filepath.ToSlash(prefix)))
- f.excluded = append(f.excluded, op == '-')
- }
-
- return &f
-}
-
-// Disallow return true if the path is excluded from the filterer's filters.
-func (f *Filterer) Disallow(path string) bool {
- // Ensure trailing but not leading slash.
- path = strings.TrimPrefix(path, "/")
- if !strings.HasSuffix(path, "/") {
- path += "/"
- }
-
- // TODO(adonovan): opt: iterate in reverse and break at first match.
- excluded := false
- for i, filter := range f.filters {
- if filter.MatchString(path) {
- excluded = f.excluded[i] // last match wins
- }
- }
- return excluded
-}
-
-// convertFilterToRegexp replaces glob-like operator substrings in a string file path to their equivalent regex forms.
-// Supporting glob-like operators:
-// - **: match zero or more complete path segments
-func convertFilterToRegexp(filter string) *regexp.Regexp {
- if filter == "" {
- return regexp.MustCompile(".*")
- }
- var ret strings.Builder
- ret.WriteString("^")
- segs := strings.Split(filter, "/")
- for _, seg := range segs {
- // Inv: seg != "" since path is clean.
- if seg == "**" {
- ret.WriteString(".*")
- } else {
- ret.WriteString(regexp.QuoteMeta(seg))
- }
- ret.WriteString("/")
- }
- pattern := ret.String()
-
- // Remove unnecessary "^.*" prefix, which increased
- // BenchmarkWorkspaceSymbols time by ~20% (even though
- // filter CPU time increased by only by ~2.5%) when the
- // default filter was changed to "**/node_modules".
- pattern = strings.TrimPrefix(pattern, "^.*")
-
- return regexp.MustCompile(pattern)
-}
-
// symbolFile holds symbol information for a single file.
type symbolFile struct {
uri protocol.DocumentURI
md *Metadata
- syms []Symbol
+ syms []cache.Symbol
}
// matchFile scans a symbol file and adds matching symbols to the store.
diff --git a/gopls/internal/lsp/source/workspace_symbol_test.go b/gopls/internal/lsp/source/workspace_symbol_test.go
index 24fb8b4..b102a23 100644
--- a/gopls/internal/lsp/source/workspace_symbol_test.go
+++ b/gopls/internal/lsp/source/workspace_symbol_test.go
@@ -6,6 +6,8 @@
import (
"testing"
+
+ "golang.org/x/tools/gopls/internal/lsp/cache"
)
func TestParseQuery(t *testing.T) {
@@ -120,7 +122,7 @@
}
for _, test := range tests {
- filterer := NewFilterer(test.filters)
+ filterer := cache.NewFilterer(test.filters)
for _, inc := range test.included {
if filterer.Disallow(inc) {
t.Errorf("Filters %v excluded %v, wanted included", test.filters, inc)
diff --git a/gopls/internal/lsp/template/highlight.go b/gopls/internal/lsp/template/highlight.go
index 3d65403..ba61f0c 100644
--- a/gopls/internal/lsp/template/highlight.go
+++ b/gopls/internal/lsp/template/highlight.go
@@ -10,11 +10,11 @@
"regexp"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
)
-func Highlight(ctx context.Context, snapshot source.Snapshot, fh file.Handle, loc protocol.Position) ([]protocol.DocumentHighlight, error) {
+func Highlight(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, loc protocol.Position) ([]protocol.DocumentHighlight, error) {
buf, err := fh.Content()
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/template/implementations.go b/gopls/internal/lsp/template/implementations.go
index adf4733..c1637f7 100644
--- a/gopls/internal/lsp/template/implementations.go
+++ b/gopls/internal/lsp/template/implementations.go
@@ -14,7 +14,6 @@
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
)
// line number (1-based) and message
@@ -23,27 +22,27 @@
// Diagnose returns parse errors. There is only one.
// The errors are not always helpful. For instance { {end}}
// will likely point to the end of the file.
-func Diagnose(f file.Handle) []*source.Diagnostic {
+func Diagnose(f file.Handle) []*cache.Diagnostic {
// no need for skipTemplate check, as Diagnose is called on the
// snapshot's template files
buf, err := f.Content()
if err != nil {
// Is a Diagnostic with no Range useful? event.Error also?
msg := fmt.Sprintf("failed to read %s (%v)", f.URI().Path(), err)
- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError, URI: f.URI(),
- Source: source.TemplateError}
- return []*source.Diagnostic{&d}
+ d := cache.Diagnostic{Message: msg, Severity: protocol.SeverityError, URI: f.URI(),
+ Source: cache.TemplateError}
+ return []*cache.Diagnostic{&d}
}
p := parseBuffer(buf)
if p.ParseErr == nil {
return nil
}
- unknownError := func(msg string) []*source.Diagnostic {
+ unknownError := func(msg string) []*cache.Diagnostic {
s := fmt.Sprintf("malformed template error %q: %s", p.ParseErr.Error(), msg)
- d := source.Diagnostic{
+ d := cache.Diagnostic{
Message: s, Severity: protocol.SeverityError, Range: p.Range(p.nls[0], 1),
- URI: f.URI(), Source: source.TemplateError}
- return []*source.Diagnostic{&d}
+ URI: f.URI(), Source: cache.TemplateError}
+ return []*cache.Diagnostic{&d}
}
// errors look like `template: :40: unexpected "}" in operand`
// so the string needs to be parsed
@@ -58,8 +57,8 @@
return unknownError(msg)
}
msg := matches[2]
- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError,
- Source: source.TemplateError}
+ d := cache.Diagnostic{Message: msg, Severity: protocol.SeverityError,
+ Source: cache.TemplateError}
start := p.nls[lineno-1]
if lineno < len(p.nls) {
size := p.nls[lineno] - start
@@ -67,7 +66,7 @@
} else {
d.Range = p.Range(start, 1)
}
- return []*source.Diagnostic{&d}
+ return []*cache.Diagnostic{&d}
}
// Definition finds the definitions of the symbol at loc. It
@@ -94,7 +93,7 @@
return ans, nil
}
-func Hover(ctx context.Context, snapshot source.Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
+func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
sym, p, err := symAtPosition(fh, position)
if sym == nil || err != nil {
return nil, err
@@ -148,7 +147,7 @@
return ans, nil
}
-func SemanticTokens(ctx context.Context, snapshot source.Snapshot, spn protocol.DocumentURI, add func(line, start, len uint32), d func() []uint32) (*protocol.SemanticTokens, error) {
+func SemanticTokens(ctx context.Context, snapshot *cache.Snapshot, spn protocol.DocumentURI, add func(line, start, len uint32), d func() []uint32) (*protocol.SemanticTokens, error) {
fh, err := snapshot.ReadFile(ctx, spn)
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/template/symbols.go b/gopls/internal/lsp/template/symbols.go
index bc2e278..e0fcab8 100644
--- a/gopls/internal/lsp/template/symbols.go
+++ b/gopls/internal/lsp/template/symbols.go
@@ -12,8 +12,8 @@
"unicode/utf8"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
@@ -194,7 +194,7 @@
// DocumentSymbols returns a hierarchy of the symbols defined in a template file.
// (The hierarchy is flat. SymbolInformation might be better.)
-func DocumentSymbols(snapshot source.Snapshot, fh file.Handle) ([]protocol.DocumentSymbol, error) {
+func DocumentSymbols(snapshot *cache.Snapshot, fh file.Handle) ([]protocol.DocumentSymbol, error) {
buf, err := fh.Content()
if err != nil {
return nil, err
diff --git a/gopls/internal/lsp/work/completion.go b/gopls/internal/lsp/work/completion.go
index e450287..292dc84 100644
--- a/gopls/internal/lsp/work/completion.go
+++ b/gopls/internal/lsp/work/completion.go
@@ -14,12 +14,12 @@
"strings"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
-func Completion(ctx context.Context, snapshot source.Snapshot, fh file.Handle, position protocol.Position) (*protocol.CompletionList, error) {
+func Completion(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.CompletionList, error) {
ctx, done := event.Start(ctx, "work.Completion")
defer done()
diff --git a/gopls/internal/lsp/work/diagnostics.go b/gopls/internal/lsp/work/diagnostics.go
index 2c4d268..f9aea6f 100644
--- a/gopls/internal/lsp/work/diagnostics.go
+++ b/gopls/internal/lsp/work/diagnostics.go
@@ -14,15 +14,14 @@
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
-func Diagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*source.Diagnostic, error) {
+func Diagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) {
ctx, done := event.Start(ctx, "work.Diagnostics", snapshot.Labels()...)
defer done()
- reports := map[protocol.DocumentURI][]*source.Diagnostic{}
+ reports := map[protocol.DocumentURI][]*cache.Diagnostic{}
uri := snapshot.WorkFile()
if uri == "" {
return nil, nil
@@ -31,7 +30,7 @@
if err != nil {
return nil, err
}
- reports[fh.URI()] = []*source.Diagnostic{}
+ reports[fh.URI()] = []*cache.Diagnostic{}
diagnostics, err := DiagnosticsForWork(ctx, snapshot, fh)
if err != nil {
return nil, err
@@ -47,7 +46,7 @@
return reports, nil
}
-func DiagnosticsForWork(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]*source.Diagnostic, error) {
+func DiagnosticsForWork(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]*cache.Diagnostic, error) {
pw, err := snapshot.ParseWork(ctx, fh)
if err != nil {
if pw == nil || len(pw.ParseErrors) == 0 {
@@ -57,7 +56,7 @@
}
// Add diagnostic if a directory does not contain a module.
- var diagnostics []*source.Diagnostic
+ var diagnostics []*cache.Diagnostic
for _, use := range pw.File.Use {
rng, err := pw.Mapper.OffsetRange(use.Syntax.Start.Byte, use.Syntax.End.Byte)
if err != nil {
@@ -69,11 +68,11 @@
return nil, err
}
if _, err := modfh.Content(); err != nil && os.IsNotExist(err) {
- diagnostics = append(diagnostics, &source.Diagnostic{
+ diagnostics = append(diagnostics, &cache.Diagnostic{
URI: fh.URI(),
Range: rng,
Severity: protocol.SeverityError,
- Source: source.WorkFileError,
+ Source: cache.WorkFileError,
Message: fmt.Sprintf("directory %v does not contain a module", use.Path),
})
}
@@ -81,7 +80,7 @@
return diagnostics, nil
}
-func modFileURI(pw *source.ParsedWorkFile, use *modfile.Use) protocol.DocumentURI {
+func modFileURI(pw *cache.ParsedWorkFile, use *modfile.Use) protocol.DocumentURI {
workdir := filepath.Dir(pw.URI.Path())
modroot := filepath.FromSlash(use.Path)
diff --git a/gopls/internal/lsp/work/format.go b/gopls/internal/lsp/work/format.go
index ef86952..1c081a8 100644
--- a/gopls/internal/lsp/work/format.go
+++ b/gopls/internal/lsp/work/format.go
@@ -9,12 +9,12 @@
"golang.org/x/mod/modfile"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
-func Format(ctx context.Context, snapshot source.Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
+func Format(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.TextEdit, error) {
ctx, done := event.Start(ctx, "work.Format")
defer done()
diff --git a/gopls/internal/lsp/work/hover.go b/gopls/internal/lsp/work/hover.go
index 243df51..5295ae0 100644
--- a/gopls/internal/lsp/work/hover.go
+++ b/gopls/internal/lsp/work/hover.go
@@ -13,7 +13,6 @@
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/event"
)
@@ -74,7 +73,7 @@
}, nil
}
-func usePath(pw *source.ParsedWorkFile, offset int) (use *modfile.Use, pathStart, pathEnd int) {
+func usePath(pw *cache.ParsedWorkFile, offset int) (use *modfile.Use, pathStart, pathEnd int) {
for _, u := range pw.File.Use {
path := []byte(u.Path)
s, e := u.Syntax.Start.Byte, u.Syntax.End.Byte
diff --git a/gopls/internal/lsp/workspace_symbol.go b/gopls/internal/lsp/workspace_symbol.go
index c85dbc3..87891b7 100644
--- a/gopls/internal/lsp/workspace_symbol.go
+++ b/gopls/internal/lsp/workspace_symbol.go
@@ -7,6 +7,7 @@
import (
"context"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/telemetry"
@@ -26,7 +27,7 @@
matcher := s.Options().SymbolMatcher
style := s.Options().SymbolStyle
- var snapshots []source.Snapshot
+ var snapshots []*cache.Snapshot
for _, v := range views {
snapshot, release, err := v.Snapshot()
if err != nil {
diff --git a/gopls/internal/regtest/diagnostics/golist_test.go b/gopls/internal/regtest/diagnostics/golist_test.go
index 85b35be..d9d83ef 100644
--- a/gopls/internal/regtest/diagnostics/golist_test.go
+++ b/gopls/internal/regtest/diagnostics/golist_test.go
@@ -7,8 +7,8 @@
import (
"testing"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/testenv"
)
@@ -54,16 +54,16 @@
InitialWorkspaceLoad,
Diagnostics(
env.AtRegexp("a/a.go", "import\n()"),
- FromSource(string(source.ParseError)),
+ FromSource(string(cache.ParseError)),
),
Diagnostics(
AtPosition("c/c.go", 0, 0),
- FromSource(string(source.ListError)),
+ FromSource(string(cache.ListError)),
WithMessage("may indicate failure to perform cgo processing"),
),
Diagnostics(
env.AtRegexp("p/p.go", `"a.com/q"`),
- FromSource(string(source.ListError)),
+ FromSource(string(cache.ListError)),
WithMessage("import cycle not allowed"),
),
)
diff --git a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/misc/vuln_test.go
index 4ba2eb5..f02d765 100644
--- a/gopls/internal/regtest/misc/vuln_test.go
+++ b/gopls/internal/regtest/misc/vuln_test.go
@@ -21,10 +21,8 @@
"golang.org/x/tools/gopls/internal/lsp/command"
"golang.org/x/tools/gopls/internal/lsp/protocol"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/lsp/tests/compare"
"golang.org/x/tools/gopls/internal/vulncheck"
- "golang.org/x/tools/gopls/internal/vulncheck/scan"
"golang.org/x/tools/gopls/internal/vulncheck/vulntest"
)
@@ -196,7 +194,7 @@
// When fetchinging stdlib package vulnerability info,
// behave as if our go version is go1.18 for this testing.
// The default behavior is to run `go env GOVERSION` (which isn't mutable env var).
- scan.GoVersionForVulnTest: "go1.18",
+ cache.GoVersionForVulnTest: "go1.18",
"_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`.
},
Settings{
@@ -251,7 +249,7 @@
"GOVULNDB": db.URI(),
// When fetchinging stdlib package vulnerability info,
// behave as if our go version is go1.18 for this testing.
- scan.GoVersionForVulnTest: "go1.18",
+ cache.GoVersionForVulnTest: "go1.18",
"_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`.
},
Settings{"ui.diagnostic.vulncheck": "Imports"},
@@ -452,7 +450,7 @@
// When fetching stdlib package vulnerability info,
// behave as if our go version is go1.18 for this testing.
// The default behavior is to run `go env GOVERSION` (which isn't mutable env var).
- scan.GoVersionForVulnTest: "go1.18",
+ cache.GoVersionForVulnTest: "go1.18",
"_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`.
"GOSUMDB": "off",
}
@@ -488,7 +486,7 @@
{
msg: "golang.org/amod has known vulnerabilities GO-2022-01, GO-2022-03.",
severity: protocol.SeverityInformation,
- source: string(source.Vulncheck),
+ source: string(cache.Vulncheck),
codeActions: []string{
"Run govulncheck to verify",
"Upgrade to v1.0.6",
@@ -508,7 +506,7 @@
{
msg: "golang.org/bmod has a vulnerability GO-2022-02.",
severity: protocol.SeverityInformation,
- source: string(source.Vulncheck),
+ source: string(cache.Vulncheck),
codeActions: []string{
"Run govulncheck to verify",
},
@@ -673,7 +671,7 @@
{
msg: "golang.org/amod has a vulnerability used in the code: GO-2022-01.",
severity: protocol.SeverityWarning,
- source: string(source.Govulncheck),
+ source: string(cache.Govulncheck),
codeActions: []string{
"Upgrade to v1.0.4",
"Upgrade to latest",
@@ -683,7 +681,7 @@
{
msg: "golang.org/amod has a vulnerability GO-2022-03 that is not used in the code.",
severity: protocol.SeverityInformation,
- source: string(source.Govulncheck),
+ source: string(cache.Govulncheck),
codeActions: []string{
"Upgrade to v1.0.6",
"Upgrade to latest",
@@ -703,7 +701,7 @@
{
msg: "golang.org/bmod has a vulnerability used in the code: GO-2022-02.",
severity: protocol.SeverityWarning,
- source: string(source.Govulncheck),
+ source: string(cache.Govulncheck),
codeActions: []string{
"Reset govulncheck result", // no fix, but we should give an option to reset.
},
@@ -826,7 +824,7 @@
{
msg: "golang.org/bmod has a vulnerability GO-2022-02 that is not used in the code.",
severity: protocol.SeverityInformation,
- source: string(source.Govulncheck),
+ source: string(cache.Govulncheck),
codeActions: []string{
"Reset govulncheck result",
},
diff --git a/gopls/internal/vulncheck/scan/command.go b/gopls/internal/vulncheck/scan/command.go
index a75e677..06cd6a5 100644
--- a/gopls/internal/vulncheck/scan/command.go
+++ b/gopls/internal/vulncheck/scan/command.go
@@ -15,26 +15,16 @@
"os"
"os/exec"
"sort"
- "strings"
- "sync"
"time"
- "golang.org/x/mod/semver"
"golang.org/x/sync/errgroup"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/gopls/internal/lsp/source"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/vulncheck"
"golang.org/x/tools/gopls/internal/vulncheck/govulncheck"
"golang.org/x/tools/gopls/internal/vulncheck/osv"
- isem "golang.org/x/tools/gopls/internal/vulncheck/semver"
"golang.org/x/vuln/scan"
)
-// GoVersionForVulnTest is an internal environment variable used in gopls
-// testing to examine govulncheck behavior with a go version different
-// than what `go version` returns in the system.
-const GoVersionForVulnTest = "_GOPLS_TEST_VULNCHECK_GOVERSION"
-
// Main implements gopls vulncheck.
func Main(ctx context.Context, args ...string) error {
// wrapping govulncheck.
@@ -51,7 +41,7 @@
//
// TODO(rfindley): this should accept a *View (which exposes) Options, rather
// than a snapshot.
-func RunGovulncheck(ctx context.Context, pattern string, snapshot source.Snapshot, dir string, log io.Writer) (*vulncheck.Result, error) {
+func RunGovulncheck(ctx context.Context, pattern string, snapshot *cache.Snapshot, dir string, log io.Writer) (*vulncheck.Result, error) {
vulncheckargs := []string{
"vulncheck", "--",
"-json",
@@ -61,7 +51,7 @@
if dir != "" {
vulncheckargs = append(vulncheckargs, "-C", dir)
}
- if db := getEnv(snapshot, "GOVULNDB"); db != "" {
+ if db := cache.GetEnv(snapshot, "GOVULNDB"); db != "" {
vulncheckargs = append(vulncheckargs, "-db", db)
}
vulncheckargs = append(vulncheckargs, pattern)
@@ -81,7 +71,7 @@
cmd := exec.CommandContext(ctx, os.Args[0], vulncheckargs...)
cmd.Env = getEnvSlices(snapshot)
- if goversion := getEnv(snapshot, GoVersionForVulnTest); goversion != "" {
+ if goversion := cache.GetEnv(snapshot, cache.GoVersionForVulnTest); goversion != "" {
// Let govulncheck API use a different Go version using the (undocumented) hook
// in https://go.googlesource.com/vuln/+/v1.0.1/internal/scan/run.go#76
cmd.Env = append(cmd.Env, "GOVERSION="+goversion)
@@ -174,306 +164,6 @@
return nil
}
-func getEnv(snapshot source.Snapshot, key string) string {
- val, ok := snapshot.Options().Env[key]
- if ok {
- return val
- }
- return os.Getenv(key)
-}
-
-func getEnvSlices(snapshot source.Snapshot) []string {
+func getEnvSlices(snapshot *cache.Snapshot) []string {
return append(os.Environ(), snapshot.Options().EnvSlice()...)
}
-
-// semverToGoTag returns the Go standard library repository tag corresponding
-// to semver, a version string without the initial "v".
-// Go tags differ from standard semantic versions in a few ways,
-// such as beginning with "go" instead of "v".
-func semverToGoTag(v string) string {
- if strings.HasPrefix(v, "v0.0.0") {
- return "master"
- }
- // Special case: v1.0.0 => go1.
- if v == "v1.0.0" {
- return "go1"
- }
- if !semver.IsValid(v) {
- return fmt.Sprintf("<!%s:invalid semver>", v)
- }
- goVersion := semver.Canonical(v)
- prerelease := semver.Prerelease(goVersion)
- versionWithoutPrerelease := strings.TrimSuffix(goVersion, prerelease)
- patch := strings.TrimPrefix(versionWithoutPrerelease, semver.MajorMinor(goVersion)+".")
- if patch == "0" {
- versionWithoutPrerelease = strings.TrimSuffix(versionWithoutPrerelease, ".0")
- }
- goVersion = fmt.Sprintf("go%s", strings.TrimPrefix(versionWithoutPrerelease, "v"))
- if prerelease != "" {
- // Go prereleases look like "beta1" instead of "beta.1".
- // "beta1" is bad for sorting (since beta10 comes before beta9), so
- // require the dot form.
- i := finalDigitsIndex(prerelease)
- if i >= 1 {
- if prerelease[i-1] != '.' {
- return fmt.Sprintf("<!%s:final digits in a prerelease must follow a period>", v)
- }
- // Remove the dot.
- prerelease = prerelease[:i-1] + prerelease[i:]
- }
- goVersion += strings.TrimPrefix(prerelease, "-")
- }
- return goVersion
-}
-
-// finalDigitsIndex returns the index of the first digit in the sequence of digits ending s.
-// If s doesn't end in digits, it returns -1.
-func finalDigitsIndex(s string) int {
- // Assume ASCII (since the semver package does anyway).
- var i int
- for i = len(s) - 1; i >= 0; i-- {
- if s[i] < '0' || s[i] > '9' {
- break
- }
- }
- if i == len(s)-1 {
- return -1
- }
- return i + 1
-}
-
-// VulnerablePackages queries the vulndb and reports which vulnerabilities
-// apply to this snapshot. The result contains a set of packages,
-// grouped by vuln ID and by module. This implements the "import-based"
-// vulnerability report on go.mod files.
-func VulnerablePackages(ctx context.Context, snapshot source.Snapshot) (*vulncheck.Result, error) {
- // TODO(hyangah): can we let 'govulncheck' take a package list
- // used in the workspace and implement this function?
-
- // We want to report the intersection of vulnerable packages in the vulndb
- // and packages transitively imported by this module ('go list -deps all').
- // We use snapshot.AllMetadata to retrieve the list of packages
- // as an approximation.
- //
- // TODO(hyangah): snapshot.AllMetadata is a superset of
- // `go list all` - e.g. when the workspace has multiple main modules
- // (multiple go.mod files), that can include packages that are not
- // used by this module. Vulncheck behavior with go.work is not well
- // defined. Figure out the meaning, and if we decide to present
- // the result as if each module is analyzed independently, make
- // gopls track a separate build list for each module and use that
- // information instead of snapshot.AllMetadata.
- metadata, err := snapshot.AllMetadata(ctx)
- if err != nil {
- return nil, err
- }
-
- // TODO(hyangah): handle vulnerabilities in the standard library.
-
- // Group packages by modules since vuln db is keyed by module.
- metadataByModule := map[source.PackagePath][]*source.Metadata{}
- for _, md := range metadata {
- modulePath := source.PackagePath(osv.GoStdModulePath)
- if mi := md.Module; mi != nil {
- modulePath = source.PackagePath(mi.Path)
- }
- metadataByModule[modulePath] = append(metadataByModule[modulePath], md)
- }
-
- var (
- mu sync.Mutex
- // Keys are osv.Entry.ID
- osvs = map[string]*osv.Entry{}
- findings []*govulncheck.Finding
- )
-
- goVersion := snapshot.Options().Env[GoVersionForVulnTest]
- if goVersion == "" {
- goVersion = snapshot.GoVersionString()
- }
-
- stdlibModule := &packages.Module{
- Path: osv.GoStdModulePath,
- Version: goVersion,
- }
-
- // GOVULNDB may point the test db URI.
- db := getEnv(snapshot, "GOVULNDB")
-
- var group errgroup.Group
- group.SetLimit(10) // limit govulncheck api runs
- for _, mds := range metadataByModule {
- mds := mds
- group.Go(func() error {
- effectiveModule := stdlibModule
- if m := mds[0].Module; m != nil {
- effectiveModule = m
- }
- for effectiveModule.Replace != nil {
- effectiveModule = effectiveModule.Replace
- }
- ver := effectiveModule.Version
- if ver == "" || !isem.Valid(ver) {
- // skip invalid version strings. the underlying scan api is strict.
- return nil
- }
-
- // TODO(hyangah): batch these requests and add in-memory cache for efficiency.
- vulns, err := osvsByModule(ctx, db, effectiveModule.Path+"@"+ver)
- if err != nil {
- return err
- }
- if len(vulns) == 0 { // No known vulnerability.
- return nil
- }
-
- // set of packages in this module known to gopls.
- // This will be lazily initialized when we need it.
- var knownPkgs map[source.PackagePath]bool
-
- // Report vulnerabilities that affect packages of this module.
- for _, entry := range vulns {
- var vulnerablePkgs []*govulncheck.Finding
- fixed := fixedVersion(effectiveModule.Path, entry.Affected)
-
- for _, a := range entry.Affected {
- if a.Module.Ecosystem != osv.GoEcosystem || a.Module.Path != effectiveModule.Path {
- continue
- }
- for _, imp := range a.EcosystemSpecific.Packages {
- if knownPkgs == nil {
- knownPkgs = toPackagePathSet(mds)
- }
- if knownPkgs[source.PackagePath(imp.Path)] {
- vulnerablePkgs = append(vulnerablePkgs, &govulncheck.Finding{
- OSV: entry.ID,
- FixedVersion: fixed,
- Trace: []*govulncheck.Frame{
- {
- Module: effectiveModule.Path,
- Version: effectiveModule.Version,
- Package: imp.Path,
- },
- },
- })
- }
- }
- }
- if len(vulnerablePkgs) == 0 {
- continue
- }
- mu.Lock()
- osvs[entry.ID] = entry
- findings = append(findings, vulnerablePkgs...)
- mu.Unlock()
- }
- return nil
- })
- }
- if err := group.Wait(); err != nil {
- return nil, err
- }
-
- // Sort so the results are deterministic.
- sort.Slice(findings, func(i, j int) bool {
- x, y := findings[i], findings[j]
- if x.OSV != y.OSV {
- return x.OSV < y.OSV
- }
- return x.Trace[0].Package < y.Trace[0].Package
- })
- ret := &vulncheck.Result{
- Entries: osvs,
- Findings: findings,
- Mode: vulncheck.ModeImports,
- }
- return ret, nil
-}
-
-// toPackagePathSet transforms the metadata to a set of package paths.
-func toPackagePathSet(mds []*source.Metadata) map[source.PackagePath]bool {
- pkgPaths := make(map[source.PackagePath]bool, len(mds))
- for _, md := range mds {
- pkgPaths[md.PkgPath] = true
- }
- return pkgPaths
-}
-
-func fixedVersion(modulePath string, affected []osv.Affected) string {
- fixed := LatestFixed(modulePath, affected)
- if fixed != "" {
- fixed = versionString(modulePath, fixed)
- }
- return fixed
-}
-
-// versionString prepends a version string prefix (`v` or `go`
-// depending on the modulePath) to the given semver-style version string.
-func versionString(modulePath, version string) string {
- if version == "" {
- return ""
- }
- v := "v" + version
- // These are internal Go module paths used by the vuln DB
- // when listing vulns in standard library and the go command.
- if modulePath == "stdlib" || modulePath == "toolchain" {
- return semverToGoTag(v)
- }
- return v
-}
-
-// osvsByModule runs a govulncheck database query.
-func osvsByModule(ctx context.Context, db, moduleVersion string) ([]*osv.Entry, error) {
- var args []string
- args = append(args, "-mode=query", "-json")
- if db != "" {
- args = append(args, "-db="+db)
- }
- args = append(args, moduleVersion)
-
- ir, iw := io.Pipe()
- handler := &osvReader{}
-
- var g errgroup.Group
- g.Go(func() error {
- defer iw.Close() // scan API doesn't close cmd.Stderr/cmd.Stdout.
- cmd := scan.Command(ctx, args...)
- cmd.Stdout = iw
- // TODO(hakim): Do we need to set cmd.Env = getEnvSlices(),
- // or is the process environment good enough?
- if err := cmd.Start(); err != nil {
- return err
- }
- return cmd.Wait()
- })
- g.Go(func() error {
- return govulncheck.HandleJSON(ir, handler)
- })
-
- if err := g.Wait(); err != nil {
- return nil, err
- }
- return handler.entry, nil
-}
-
-// osvReader implements govulncheck.Handler.
-type osvReader struct {
- entry []*osv.Entry
-}
-
-func (h *osvReader) OSV(entry *osv.Entry) error {
- h.entry = append(h.entry, entry)
- return nil
-}
-
-func (h *osvReader) Config(config *govulncheck.Config) error {
- return nil
-}
-
-func (h *osvReader) Finding(finding *govulncheck.Finding) error {
- return nil
-}
-
-func (h *osvReader) Progress(progress *govulncheck.Progress) error {
- return nil
-}
diff --git a/gopls/internal/vulncheck/scan/util.go b/gopls/internal/vulncheck/scan/util.go
deleted file mode 100644
index 2ea75a5..0000000
--- a/gopls/internal/vulncheck/scan/util.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2022 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.
-
-//go:build go1.18
-// +build go1.18
-
-package scan
-
-import (
- "golang.org/x/mod/semver"
- "golang.org/x/tools/gopls/internal/vulncheck/osv"
- isem "golang.org/x/tools/gopls/internal/vulncheck/semver"
-)
-
-// LatestFixed returns the latest fixed version in the list of affected ranges,
-// or the empty string if there are no fixed versions.
-func LatestFixed(modulePath string, as []osv.Affected) string {
- v := ""
- for _, a := range as {
- if a.Module.Path != modulePath {
- continue
- }
- for _, r := range a.Ranges {
- if r.Type == osv.RangeTypeSemver {
- for _, e := range r.Events {
- if e.Fixed != "" && (v == "" ||
- semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) {
- v = e.Fixed
- }
- }
- }
- }
- }
- return v
-}
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Findley.
Robert Findley uploaded patch set #2 to this change.
The following approvals got outdated and were removed: LUCI-TryBot-Result-1 by Go LUCI
gopls/internal/lsp: invert the import between cache and source
After much pulling at this thread, we can finally invert the
relationship between the cache package and source package.
Change-Id: I34c9069231dd27a4fc0d88ae9d688d2a38e81d27
---
M gopls/internal/lsp/cache/analysis.go
M gopls/internal/lsp/cache/check.go
M gopls/internal/lsp/cache/diagnostics.go
M gopls/internal/lsp/cache/errors.go
A gopls/internal/lsp/cache/filterer.go
M gopls/internal/lsp/cache/metadata/metadata.go
M gopls/internal/lsp/cache/mod.go
M gopls/internal/lsp/cache/mod_tidy.go
M gopls/internal/lsp/cache/mod_vuln.go
M gopls/internal/lsp/cache/parse.go
M gopls/internal/lsp/cache/pkg.go
M gopls/internal/lsp/cache/snapshot.go
M gopls/internal/lsp/cache/symbols.go
M gopls/internal/lsp/cache/view.go
70 files changed, 1,058 insertions(+), 1,168 deletions(-)
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Findley.
Robert Findley uploaded patch set #3 to this change.
gopls/internal/lsp: invert the import between cache and source
After much pulling at this thread, finally invert the relationship
between the cache package and source package.
In order to do this, the implementation of the mod vuln promise had to
be moved from the internal/vulncheck/scan package into the cache package
(to avoid cycles). This is consistent with the implementation of other
promises in the cache package.
Attention is currently required from: Robert Findley.
Patch set 3:Code-Review +2
3 comments:
File gopls/internal/lsp/cache/mod.go:
Patch Set #3, Line 27: ParsedModule
"ParsedModFile", for symmetry with ParsedWorkFile, and because a module is more than a go.mod file.
Oh, this was preexisting. Never mind (or add TODO).
File gopls/internal/lsp/cache/snapshot.go:
no s
File gopls/internal/lsp/source/snapshot.go:
Patch Set #3, Line 39: type Snapshot interface {
yay!
Remember when I thought I could get rid of this interface in one my first CLs to gopls? ;-)
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Findley.
Robert Findley uploaded patch set #4 to this change.
The following approvals got outdated and were removed: LUCI-TryBot-Result-1 by Go LUCI
gopls/internal/lsp: invert the import between cache and source
70 files changed, 1,058 insertions(+), 1,167 deletions(-)
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Findley.
Robert Findley uploaded patch set #5 to this change.
gopls/internal/lsp: invert the import between cache and source
70 files changed, 1,057 insertions(+), 1,167 deletions(-)
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Patch Set #3, Line 27: ParsedModule
"ParsedModFile", for symmetry with ParsedWorkFile, and because a module is more than a go.mod file. […]
Acknowledged
File gopls/internal/lsp/cache/snapshot.go:
no s
Done
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Robert Findley.
Robert Findley uploaded patch set #6 to this change.
The following approvals got outdated and were removed: Commit-Queue+1 by Robert Findley
70 files changed, 1,058 insertions(+), 1,168 deletions(-)
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.
Robert Findley submitted this change.
3 is the latest approved patch-set.
The change was submitted with unreviewed changes in the following files:
```
The name of the file: gopls/internal/lsp/cache/snapshot.go
Insertions: 1, Deletions: 1.
@@ -733,7 +733,7 @@
return perFile, s.forEachPackage(ctx, ids, pre, post)
}
-// References returns cross-references indexes for the specified packages.
+// References returns cross-reference indexes for the specified packages.
//
// If these indexes cannot be loaded from cache, the requested packages may
// be type-checked.
@@ -1343,15 +1343,16 @@
defer s.mu.Unlock()
s.view.markKnown(uri)
- if fh, ok := s.files.Get(uri); ok {
- return fh, nil
- }
- fh, err := s.view.fs.ReadFile(ctx, uri)
- if err != nil {
- return nil, err
+ fh, ok := s.files.Get(uri)
+ if !ok {
+ var err error
+ fh, err = s.view.fs.ReadFile(ctx, uri)
+ if err != nil {
+ return nil, err
+ }
+ s.files.Set(uri, fh)
}
- s.files.Set(uri, fh)
return fh, nil
}
```
```
The name of the file: gopls/internal/lsp/cache/metadata/metadata.go
Insertions: 10, Deletions: 0.
@@ -5,8 +5,10 @@
package metadata
import (
+ "go/ast"
"go/types"
"sort"
+ "strconv"
"strings"
"golang.org/x/tools/go/packages"
@@ -25,18 +27,8 @@
ImportPath string // path that appears in an import declaration (e.g. "example.com/foo")
)
-// 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
-}
-
// Metadata represents package metadata retrieved from go/packages.
-// The Deps* maps do not contain self-import edges.
+// The DepsBy{Imp,Pkg}Path maps do not contain self-import edges.
//
// An ad-hoc package (without go.mod or GOPATH) has its ID, PkgPath,
// and LoadDir equal to the absolute path of its directory.
@@ -165,13 +157,14 @@
return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath
}
-// A Source maps package IDs to Packages.
+// A Source maps package IDs to metadata for the packages.
//
// TODO(rfindley): replace this with a concrete metadata graph, once it is
// exposed from the snapshot.
type Source interface {
// Metadata returns Metadata for the given package ID, or nil if it does not
// exist.
+ // TODO(rfindley): consider returning (*Metadata, bool)
Metadata(PackageID) *Metadata
}
@@ -208,6 +201,16 @@
})
}
+// UnquoteImportPath returns the unquoted import path of s,
+// or "" if the path is not properly quoted.
+func UnquoteImportPath(spec *ast.ImportSpec) ImportPath {
+ path, err := strconv.Unquote(spec.Path.Value)
+ if err != nil {
+ return ""
+ }
+ return ImportPath(path)
+}
+
// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array.
// We use a pointer to a slice make it impossible to forget to use the result.
func RemoveIntermediateTestVariants(pmetas *[]*Metadata) {
```
```
The name of the file: gopls/internal/lsp/cache/check.go
Insertions: 2, Deletions: 2.
@@ -24,7 +24,6 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
- "golang.org/x/tools/gopls/internal/immutable"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/cache/typerefs"
"golang.org/x/tools/gopls/internal/lsp/filecache"
@@ -58,14 +57,13 @@
getActivePackage(id PackageID) *Package
setActivePackage(id PackageID, pkg *Package)
}
- syntaxIndex map[PackageID]int // requested ID -> index in ids
- pre preTypeCheck
- post postTypeCheck
- handles map[PackageID]*packageHandle
- parseCache *parseCache
- fset *token.FileSet // describes all parsed or imported files
- cpulimit chan unit // concurrency limiter for CPU-bound operations
- workspacePackages immutable.Map[PackageID, PackagePath]
+ syntaxIndex map[PackageID]int // requested ID -> index in ids
+ pre preTypeCheck
+ post postTypeCheck
+ handles map[PackageID]*packageHandle
+ parseCache *parseCache
+ fset *token.FileSet // describes all parsed or imported files
+ cpulimit chan unit // concurrency limiter for CPU-bound operations
mu sync.Mutex
syntaxPackages map[PackageID]*futurePackage // results of processing a requested package; may hold (nil, nil)
@@ -787,8 +785,8 @@
// directly relate to type checking. We should perhaps move the caching of
// load diagnostics to an entirely separate component, so that Packages need
// only be concerned with parsing and type checking.
- // (nevertheless, since the lifetime of load diagnostics matches that of the
- // Metadata, it is convienient to memoize them here).
+ // (Nevertheless, since the lifetime of load diagnostics matches that of the
+ // Metadata, it is convenient to memoize them here.)
loadDiagnostics []*Diagnostic
// Local data:
@@ -932,9 +930,8 @@
// A packageHandleBuilder computes a batch of packageHandles concurrently,
// sharing computed transitive reachability sets used to compute package keys.
type packageHandleBuilder struct {
- meta *metadata.Graph
- workspacePackages immutable.Map[PackageID, PackagePath]
- s *Snapshot
+ meta *metadata.Graph
+ s *Snapshot
// nodes are assembled synchronously.
nodes map[typerefs.IndexID]*handleNode
@@ -1086,7 +1083,6 @@
// this copy. In either case, the result will be valid.
n.ph = prevPH.clone(true)
} else {
- loadDiagnostics := computeLoadDiagnostics(ctx, b.s, n.m)
// No package handle: read and analyze the package syntax.
inputs, err := b.s.typeCheckInputs(ctx, n.m)
if err != nil {
@@ -1100,7 +1096,7 @@
}
n.ph = &packageHandle{
m: n.m,
- loadDiagnostics: loadDiagnostics,
+ loadDiagnostics: computeLoadDiagnostics(ctx, b.s, n.m),
localInputs: inputs,
localKey: localPackageKey(inputs),
refs: refs,
```
gopls/internal/lsp: invert the import between cache and source
After much pulling at this thread, finally invert the relationship
between the cache package and source package.
In order to do this, the implementation of the mod vuln promise had to
be moved from the internal/vulncheck/scan package into the cache package
(to avoid cycles). This is consistent with the implementation of other
promises in the cache package.
Change-Id: I34c9069231dd27a4fc0d88ae9d688d2a38e81d27
Reviewed-on: https://go-review.googlesource.com/c/tools/+/545315
Reviewed-by: Alan Donovan <adon...@google.com>
LUCI-TryBot-Result: Go LUCI <golang...@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/analysis.go
index b791782..8fa7a25 100644
--- a/gopls/internal/lsp/cache/analysis.go
+++ b/gopls/internal/lsp/cache/analysis.go
@@ -35,6 +35,7 @@
"golang.org/x/tools/gopls/internal/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/filecache"
"golang.org/x/tools/gopls/internal/lsp/frob"
"golang.org/x/tools/gopls/internal/lsp/progress"
@@ -1001,7 +1002,7 @@
}
// (Duplicates logic from check.go.)
- if !IsValidImport(an.m.PkgPath, dep.m.PkgPath) {
+ if !metadata.IsValidImport(an.m.PkgPath, dep.m.PkgPath) {
return nil, fmt.Errorf("invalid use of internal package %s", importPath)
}
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index dbf78c7..a89d256 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -29,7 +29,6 @@
"golang.org/x/tools/gopls/internal/lsp/filecache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
- "golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/analysisinternal"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/tag"
@@ -86,7 +85,10 @@
err error
}
-// TypeCheck type-checks the specified packages.
+// 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.
//
// The resulting packages slice always contains len(ids) entries, though some
// of them may be nil if (and only if) the resulting error is non-nil.
@@ -95,8 +97,13 @@
// This is different from having type-checking errors: a failure to type-check
// indicates context cancellation or otherwise significant failure to perform
// the type-checking operation.
-func (s *Snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]Package_, error) {
- pkgs := make([]Package_, len(ids))
+//
+// In general, clients should never need to type-checked syntax for an
+// intermediate test variant (ITV) package. Callers should apply
+// RemoveIntermediateTestVariants (or equivalent) before this method, or any
+// of the potentially type-checking methods below.
+func (s *Snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]*Package, error) {
+ pkgs := make([]*Package, len(ids))
var (
needIDs []PackageID // ids to type-check
@@ -202,7 +209,7 @@
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&meta)
+ metadata.RemoveIntermediateTestVariants(&meta)
for _, m := range meta {
openPackages[m.ID] = true
}
@@ -779,8 +786,8 @@
// load diagnostics to an entirely separate component, so that Packages need
// only be concerned with parsing and type checking.
// (Nevertheless, since the lifetime of load diagnostics matches that of the
- // Metadata, it is convienient to memoize them here.)
- loadDiagnostics []*source.Diagnostic
+ // Metadata, it is convenient to memoize them here.)
+ loadDiagnostics []*Diagnostic
// Local data:
@@ -1584,7 +1591,7 @@
// e.g. missing metadata for dependencies in buildPackageHandle
return nil, missingPkgError(inputs.id, path, inputs.moduleMode)
}
- if !IsValidImport(inputs.pkgPath, depPH.m.PkgPath) {
+ if !metadata.IsValidImport(inputs.pkgPath, depPH.m.PkgPath) {
return nil, fmt.Errorf("invalid use of internal package %q", path)
}
return b.getImportPackage(ctx, id)
@@ -1832,11 +1839,11 @@
index d5020ef..073b7a5 100644
--- a/gopls/internal/lsp/cache/metadata/metadata.go
+++ b/gopls/internal/lsp/cache/metadata/metadata.go
@@ -210,3 +210,33 @@
}
return ImportPath(path)
}
+
+// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array.
+// We use a pointer to a slice make it impossible to forget to use the result.
+func RemoveIntermediateTestVariants(pmetas *[]*Metadata) {
+ metas := *pmetas
+ res := metas[:0]
+ for _, m := range metas {
+ if !m.IsIntermediateTestVariant() {
+ res = append(res, m)
+ }
+ }
+ *pmetas = res
+}
+
+// IsValidImport returns whether importPkgPath is importable
+// by pkgPath
+func IsValidImport(pkgPath, importPkgPath PackagePath) bool {
+ i := strings.LastIndex(string(importPkgPath), "/internal/")
+ if i == -1 {
+ return true
+ }
+ // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to
+ // operate on package IDs, not package paths.
+ if IsCommandLineArguments(PackageID(pkgPath)) {
+ return true
+ }
+ // TODO(rfindley): this is wrong. mod.testx/p should not be able to
+ // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q
+ return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
+}
index 0f70333..8dcb3df 100644
+ result, err := modVulnImpl(ctx, arg.(*Snapshot))
return modVuln{result, err}
})
@@ -46,3 +59,331 @@
res := v.(modVuln)
return res.result, res.err
}
+
+// GoVersionForVulnTest is an internal environment variable used in gopls
+// testing to examine govulncheck behavior with a go version different
+// than what `go version` returns in the system.
+const GoVersionForVulnTest = "_GOPLS_TEST_VULNCHECK_GOVERSION"
+
+// modVulnImpl queries the vulndb and reports which vulnerabilities
+// apply to this snapshot. The result contains a set of packages,
+// grouped by vuln ID and by module. This implements the "import-based"
+// vulnerability report on go.mod files.
+func modVulnImpl(ctx context.Context, snapshot *Snapshot) (*vulncheck.Result, error) {
diff --git a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.go
index 8d08915..0f93a08 100644
--- a/gopls/internal/lsp/cache/parse.go
+++ b/gopls/internal/lsp/cache/parse.go
@@ -15,8 +15,9 @@
"golang.org/x/tools/gopls/internal/lsp/cache/parsego"
)
-// ParseGo parses the file whose contents are provided by fh, using a cache.
+// ParseGo parses the file whose contents are provided by fh.
// The resulting tree may have been fixed up.
+// If the file is not available, returns nil and an error.
func (s *Snapshot) ParseGo(ctx context.Context, fh file.Handle, mode parser.Mode) (*ParsedGoFile, error) {
pgfs, err := s.view.parseCache.parseFiles(ctx, token.NewFileSet(), mode, false, fh)
if err != nil {
index bda65b9..c151ba1 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -54,6 +54,26 @@
"golang.org/x/tools/internal/typesinternal"
)
+// 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.
+//
+// TODO(rfindley): remove this as it should not be necessary for correctness.
+type GlobalSnapshotID uint64
+
+// A Snapshot represents the current state for a given view.
+//
+// It is first and foremost an idempotent implementation of file.Source whose
+// ReadFile method returns consistent information about the existence and
+// content of each file throughout its lifetime.
+//
+// However, the snapshot also manages additional state (such as parsed files
+// and packages) that are derived from file content.
+//
+// Snapshots are responsible for bookkeeping and invalidation of this state,
+// implemented in Snapshot.clone.
type Snapshot struct {
sequenceID uint64
globalID GlobalSnapshotID
@@ -291,6 +311,13 @@
return s.view
}
+// 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.
func (s *Snapshot) FileKind(fh file.Handle) file.Kind {
// The kind of an unsaved buffer comes from the
// TextDocumentItem.LanguageID field in the didChange event,
@@ -322,6 +349,7 @@
return file.Go
}
+// Options returns the options associated with this snapshot.
func (s *Snapshot) Options() *settings.Options {
return s.view.folder.Options
}
@@ -332,6 +360,8 @@
return s.backgroundCtx
}
+// ModFiles are the go.mod files enclosed in the snapshot's view and known
+// to the snapshot.
func (s *Snapshot) ModFiles() []protocol.DocumentURI {
var uris []protocol.DocumentURI
for modURI := range s.view.workspaceModFiles {
@@ -425,6 +455,36 @@
+// RunGoCommandDirect runs the given `go` command. Verb, Args, and
+// WorkingDir must be specified.
func (s *Snapshot) RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) {
_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
if err != nil {
@@ -637,6 +697,11 @@
typerefsKind = "typerefs"
)
+// PackageDiagnostics returns diagnostics for files contained in specified
+// packages.
+//
+// If these diagnostics cannot be loaded from cache, the requested packages
+// may be type-checked.
func (s *Snapshot) PackageDiagnostics(ctx context.Context, ids ...PackageID) (map[protocol.DocumentURI][]*Diagnostic, error) {
ctx, done := event.Start(ctx, "cache.snapshot.PackageDiagnostics")
defer done()
@@ -668,11 +733,15 @@
return perFile, s.forEachPackage(ctx, ids, pre, post)
}
-func (s *Snapshot) References(ctx context.Context, ids ...PackageID) ([]XrefIndex_, error) {
+// References returns cross-reference indexes for the specified packages.
+//
+// If these indexes cannot be loaded from cache, the requested packages may
+// be type-checked.
+func (s *Snapshot) References(ctx context.Context, ids ...PackageID) ([]XrefIndex, error) {
ctx, done := event.Start(ctx, "cache.snapshot.References")
defer done()
- indexes := make([]XrefIndex_, len(ids))
+ indexes := make([]XrefIndex, len(ids))
pre := func(i int, ph *packageHandle) bool {
data, err := filecache.Get(xrefsKind, ph.key)
if err == nil { // hit
@@ -699,6 +768,10 @@
return xrefs.Lookup(index.m, index.data, targets)
}
+// MethodSets returns method-set indexes for the specified packages.
+//
+// If these indexes cannot be loaded from cache, the requested packages may
+// be type-checked.
func (s *Snapshot) MethodSets(ctx context.Context, ids ...PackageID) ([]*methodsets.Index, error) {
ctx, done := event.Start(ctx, "cache.snapshot.MethodSets")
defer done()
@@ -720,6 +793,13 @@
return indexes, s.forEachPackage(ctx, ids, pre, post)
}
+// 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),
+// and secondarily by IsIntermediateTestVariant (false < true).
+// The result may include tests and intermediate test variants of
+// importable packages.
+// It returns an error if the context was cancelled.
func (s *Snapshot) MetadataForFile(ctx context.Context, uri protocol.DocumentURI) ([]*Metadata, error) {
if s.view.ViewType() == AdHocView {
// As described in golang/go#57209, in ad-hoc workspaces (where we load ./
@@ -820,6 +900,10 @@
func boolLess(x, y bool) bool { return !x && y } // false < true
+// 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.
func (s *Snapshot) ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*Metadata, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
@@ -1039,6 +1123,17 @@
return files
}
+// WorkspaceMetadata returns a new, unordered slice containing
+// metadata for all ordinary and test packages (but not
+// intermediate test variants) in the workspace.
+//
+// The workspace is the set of modules typically defined by a
+// go.work file. It is not transitively closed: for example,
+// the standard library is not usually part of the workspace
+// even though every module in the workspace depends on it.
+//
+// Operations that must inspect all the dependencies of the
+// workspace packages should instead use AllMetadata.
func (s *Snapshot) WorkspaceMetadata(ctx context.Context) ([]*Metadata, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
@@ -1066,7 +1161,10 @@
// Symbols extracts and returns symbol information for every file contained in
// a loaded package. It awaits snapshot loading.
//
-// TODO(rfindley): move this to the top of cache/symbols.go
+// If workspaceOnly is set, this only includes symbols from files in a
+// workspace package. Otherwise, it returns symbols from all loaded packages.
+//
+// TODO(rfindley): move to symbols.go.
func (s *Snapshot) Symbols(ctx context.Context, workspaceOnly bool) (map[protocol.DocumentURI][]Symbol, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
@@ -1124,6 +1222,14 @@
return result, nil
}
+// AllMetadata returns a new unordered array of metadata for
+// all packages known to this snapshot, which includes the
+// packages of all workspace modules plus their transitive
+// import dependencies.
+//
+// It may also contain ad-hoc packages for standalone files.
+// It includes all test variants.
+//
// TODO(rfindley): just return the metadata graph here.
func (s *Snapshot) AllMetadata(ctx context.Context) ([]*Metadata, error) {
if err := s.awaitLoaded(ctx); err != nil {
@@ -1175,6 +1281,8 @@
return protocol.URIFromPath(mod), nil
}
+// Metadata returns the metadata for the specified package,
+// or nil if it was not found.
func (s *Snapshot) Metadata(id PackageID) *Metadata {
s.mu.Lock()
defer s.mu.Unlock()
@@ -1212,6 +1320,9 @@
}
}
+// FindFile returns the FileHandle for the given URI, if it is already
+// in the given snapshot.
+// TODO(adonovan): delete this operation; use ReadFile instead.
func (s *Snapshot) FindFile(uri protocol.DocumentURI) file.Handle {
s.view.markKnown(uri)
@@ -2415,6 +2526,7 @@
return results
}
+// BuiltinFile returns information about the special builtin package.
func (s *Snapshot) BuiltinFile(ctx context.Context) (*ParsedGoFile, error) {
s.AwaitInitialized(ctx)
diff --git a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbols.go
index d4bc929..6abbd14 100644
--- a/gopls/internal/lsp/cache/symbols.go
+++ b/gopls/internal/lsp/cache/symbols.go
@@ -16,6 +16,15 @@
"golang.org/x/tools/gopls/internal/lsp/protocol"
)
+// Symbol holds a precomputed symbol value. Note: we avoid using the
+// protocol.SymbolInformation struct here in order to reduce the size of each
+// symbol.
+type Symbol struct {
+ Name string
+ Kind protocol.SymbolKind
+ Range protocol.Range
+}
+
// symbolize returns the result of symbolizing the file identified by uri, using a cache.
func (s *Snapshot) symbolize(ctx context.Context, uri protocol.DocumentURI) ([]Symbol, error) {
diff --git a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go
index 51c4585..4029af0 100644
--- a/gopls/internal/lsp/cache/view.go
+++ b/gopls/internal/lsp/cache/view.go
@@ -501,6 +501,8 @@
return buf.String()
}
+// RunProcessEnvFunc runs fn with the process env for this snapshot's view.
+// Note: the process env contains cached module and filesystem state.
func (s *Snapshot) RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error {
return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
index 8024184..6073199 100644
--- a/gopls/internal/lsp/command.go
+++ b/gopls/internal/lsp/command.go
@@ -367,7 +367,7 @@
// If golang/go#44119 is resolved, go mod vendor will instead modify
// modules.txt in-place. In that case we could theoretically allow this
// command to run concurrently.
- err := deps.snapshot.RunGoCommandPiped(ctx, source.Normal|source.AllowNetwork, &gocommand.Invocation{
+ err := deps.snapshot.RunGoCommandPiped(ctx, cache.Normal|cache.AllowNetwork, &gocommand.Invocation{
Verb: "mod",
Args: []string{"vendor"},
WorkingDir: filepath.Dir(args.URI.Path()),
@@ -451,7 +451,7 @@
// dropDependency returns the edits to remove the given require from the go.mod
// file.
-func dropDependency(snapshot source.Snapshot, pm *source.ParsedModule, modulePath string) ([]protocol.TextEdit, error) {
+func dropDependency(snapshot *cache.Snapshot, pm *cache.ParsedModule, modulePath string) ([]protocol.TextEdit, error) {
// We need a private copy of the parsed go.mod file, since we're going to
// modify it.
copied, err := modfile.Parse("", pm.Mapper.Content, nil)
@@ -511,7 +511,7 @@
Args: []string{pkgPath, "-v", "-count=1", "-run", fmt.Sprintf("^%s$", funcName)},
WorkingDir: filepath.Dir(uri.Path()),
}
- if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
+ if err := snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, out, out); err != nil {
if errors.Is(err, context.Canceled) {
return err
}
@@ -527,7 +527,7 @@
Args: []string{pkgPath, "-v", "-run=^$", "-bench", fmt.Sprintf("^%s$", funcName)},
WorkingDir: filepath.Dir(uri.Path()),
}
- if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
+ if err := snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, out, out); err != nil {
if errors.Is(err, context.Canceled) {
return err
}
@@ -587,7 +587,7 @@
WorkingDir: args.Dir.Path(),
}
stderr := io.MultiWriter(er, progress.NewWorkDoneWriter(ctx, deps.work))
- if err := deps.snapshot.RunGoCommandPiped(ctx, source.Normal, inv, er, stderr); err != nil {
+ if err := deps.snapshot.RunGoCommandPiped(ctx, cache.Normal, inv, er, stderr); err != nil {
return err
}
return nil
@@ -600,7 +600,7 @@
progress: "Running go get",
}, func(ctx context.Context, deps commandDeps) error {
// Run on a throwaway go.mod, otherwise it'll write to the real one.
- stdout, err := deps.snapshot.RunGoCommandDirect(ctx, source.WriteTemporaryModFile|source.AllowNetwork, &gocommand.Invocation{
+ stdout, err := deps.snapshot.RunGoCommandDirect(ctx, cache.WriteTemporaryModFile|cache.AllowNetwork, &gocommand.Invocation{
Verb: "list",
Args: []string{"-f", "{{.Module.Path}}@{{.Module.Version}}", args.Pkg},
WorkingDir: filepath.Dir(args.URI.Path()),
@@ -731,8 +731,8 @@
index 2b43d8d..601b217 100644
index 6656c7f..ef8c89b 100644
--- a/gopls/internal/lsp/semantic.go
+++ b/gopls/internal/lsp/semantic.go
@@ -19,6 +19,7 @@
"time"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -231,7 +232,7 @@
pgf *source.ParsedGoFile
start, end token.Pos // range of interest
ti *types.Info
- pkg source.Package
+ pkg *cache.Package
fset *token.FileSet
// path from the root of the parse tree, used for debugging
stack []ast.Node
diff --git a/gopls/internal/lsp/server.go b/gopls/internal/lsp/server.go
index 279f4c6..27f8c88 100644
--- a/gopls/internal/lsp/server.go
+++ b/gopls/internal/lsp/server.go
@@ -183,7 +183,7 @@
// efficient to compute the set of packages and TypeCheck and
// Analyze them all at once. Or instead support textDocument/diagnostic
// (golang/go#60122).
-func (s *server) diagnoseFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (file.Handle, []*source.Diagnostic, error) {
+func (s *server) diagnoseFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (file.Handle, []*cache.Diagnostic, error) {
fh, err := snapshot.ReadFile(ctx, uri)
if err != nil {
return nil, nil, err
@@ -200,7 +200,7 @@
index b9088c7..07127a9 100644
--- a/gopls/internal/lsp/source/completion/completion.go
+++ b/gopls/internal/lsp/source/completion/completion.go
@@ -29,6 +29,7 @@
"golang.org/x/tools/go/ast/astutil"
goplsastutil "golang.org/x/tools/gopls/internal/astutil"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -168,8 +169,8 @@
// completer contains the necessary information for a single completion request.
type completer struct {
- snapshot source.Snapshot
- pkg source.Package
+ snapshot *cache.Snapshot
+ pkg *cache.Package
qf types.Qualifier // for qualifying typed expressions
mq source.MetadataQualifier // for syntactic qualifying
opts *completionOptions
@@ -444,7 +445,7 @@
index 4a7db22..fc43b82 100644
--- a/gopls/internal/lsp/source/definition.go
+++ b/gopls/internal/lsp/source/definition.go
@@ -15,13 +15,14 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)
// Definition handles the textDocument/definition request for Go files.
-func Definition(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
+func Definition(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) ([]protocol.Location, error) {
ctx, done := event.Start(ctx, "source.Definition")
defer done()
@@ -94,7 +95,7 @@
// builtinDefinition returns the location of the fake source
// declaration of a built-in in {builtin,unsafe}.go.
-func builtinDefinition(ctx context.Context, snapshot Snapshot, obj types.Object) ([]protocol.Location, error) {
+func builtinDefinition(ctx context.Context, snapshot *cache.Snapshot, obj types.Object) ([]protocol.Location, error) {
// getDecl returns the file-level declaration of name
// using legacy (go/ast) object resolution.
getDecl := func(file *ast.File, name string) (ast.Node, error) {
@@ -185,7 +186,7 @@
// TODO(rfindley): this function exists to preserve the pre-existing behavior
// of source.Identifier. Eliminate this helper in favor of sharing
// functionality with objectsAt, after choosing suitable primitives.
-func referencedObject(pkg Package, pgf *ParsedGoFile, pos token.Pos) (*ast.Ident, types.Object, types.Type) {
+func referencedObject(pkg *cache.Package, pgf *ParsedGoFile, pos token.Pos) (*ast.Ident, types.Object, types.Type) {
path := pathEnclosingObjNode(pgf.File, pos)
if len(path) == 0 {
return nil, nil, nil
@@ -225,7 +226,7 @@
index d7b5953..4ecce3b 100644
--- a/gopls/internal/lsp/source/hover.go
+++ b/gopls/internal/lsp/source/hover.go
@@ -26,6 +26,7 @@
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -64,7 +65,7 @@
}
// Hover implements the "textDocument/hover" RPC for Go files.
-func Hover(ctx context.Context, snapshot Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
+func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
ctx, done := event.Start(ctx, "source.Hover")
defer done()
@@ -91,7 +92,7 @@
// hover computes hover information at the given position. If we do not support
// hovering at the position, it returns _, nil, nil: an error is only returned
// if the position is valid but we fail to compute hover information.
-func hover(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position) (protocol.Range, *HoverJSON, error) {
+func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) (protocol.Range, *HoverJSON, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return protocol.Range{}, nil, err
@@ -367,7 +368,7 @@
// hoverBuiltin computes hover information when hovering over a builtin
// identifier.
-func hoverBuiltin(ctx context.Context, snapshot Snapshot, obj types.Object) (*HoverJSON, error) {
+func hoverBuiltin(ctx context.Context, snapshot *cache.Snapshot, obj types.Object) (*HoverJSON, error) {
// TODO(rfindley): link to the correct version of Go documentation.
builtin, err := snapshot.BuiltinFile(ctx)
if err != nil {
@@ -437,7 +438,7 @@
// imp in the file pgf of pkg.
//
// If we do not have metadata for the hovered import, it returns _
-func hoverImport(ctx context.Context, snapshot Snapshot, pkg Package, pgf *ParsedGoFile, imp *ast.ImportSpec) (protocol.Range, *HoverJSON, error) {
+func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *ParsedGoFile, imp *ast.ImportSpec) (protocol.Range, *HoverJSON, error) {
rng, err := pgf.NodeRange(imp.Path)
if err != nil {
return protocol.Range{}, nil, err
@@ -488,7 +489,7 @@
// hoverPackageName computes hover information for the package name of the file
// pgf in pkg.
-func hoverPackageName(pkg Package, pgf *ParsedGoFile) (protocol.Range, *HoverJSON, error) {
+func hoverPackageName(pkg *cache.Package, pgf *ParsedGoFile) (protocol.Range, *HoverJSON, error) {
var comment *ast.CommentGroup
for _, pgf := range pkg.CompiledGoFiles() {
if pgf.File.Doc != nil {
@@ -762,7 +763,7 @@
// fset provides file/line information).
//
// TODO(rfindley): there appears to be zero(!) tests for this functionality.
-func HoverDocForObject(ctx context.Context, snapshot Snapshot, fset *token.FileSet, obj types.Object) (*ast.CommentGroup, error) {
+func HoverDocForObject(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, obj types.Object) (*ast.CommentGroup, error) {
if _, isTypeName := obj.(*types.TypeName); isTypeName {
if _, isTypeParam := obj.Type().(*typeparams.TypeParam); isTypeParam {
return nil, nil
@@ -819,7 +820,7 @@
//
// It returns the resulting ParsedGoFile as well as new pos contained in the
// parsed file.
-func parseFull(ctx context.Context, snapshot Snapshot, fset *token.FileSet, pos token.Pos) (*ParsedGoFile, token.Pos, error) {
+func parseFull(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, pos token.Pos) (*ParsedGoFile, token.Pos, error) {
f := fset.File(pos)
if f == nil {
return nil, 0, bug.Errorf("internal error: no file for position %d", pos)
@@ -900,7 +901,7 @@
if !options.LinksInHover || options.LinkTarget == "" || h.LinkPath == "" {
return ""
}
- plainLink := BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor)
+ plainLink := cache.BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor)
switch options.PreferredContentFormat {
case protocol.Markdown:
return fmt.Sprintf("[`%s` on %s](%s)", h.SymbolName, options.LinkTarget, plainLink)
@@ -911,15 +912,6 @@
index 71d52fa..9fae9f2 100644
--- a/gopls/internal/lsp/source/known_packages.go
+++ b/gopls/internal/lsp/source/known_packages.go
@@ -14,6 +14,7 @@
"time"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/imports"
@@ -25,7 +26,7 @@
// all dot-free paths (standard packages) appear before dotful ones.
//
// It is part of the gopls.list_known_packages command.
-func KnownPackagePaths(ctx context.Context, snapshot Snapshot, fh file.Handle) ([]PackagePath, error) {
+func KnownPackagePaths(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]PackagePath, error) {
// This algorithm is expressed in terms of Metadata, not Packages,
// so it doesn't cause or wait for type checking.
@@ -75,7 +76,7 @@
continue
}
// make sure internal packages are importable by the file
- if !IsValidImport(current.PkgPath, knownPkg.PkgPath) {
+ if !metadata.IsValidImport(current.PkgPath, knownPkg.PkgPath) {
index a7631e1..16d26ef 100644
--- a/gopls/internal/lsp/source/references.go
+++ b/gopls/internal/lsp/source/references.go
@@ -27,6 +27,7 @@
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/cache/methodsets"
"golang.org/x/tools/gopls/internal/lsp/protocol"
@@ -37,7 +38,7 @@
// References returns a list of all references (sorted with
// definitions before uses) to the object denoted by the identifier at
// the given file/position, searching the entire workspace.
-func References(ctx context.Context, snapshot Snapshot, fh file.Handle, pp protocol.Position, includeDeclaration bool) ([]protocol.Location, error) {
+func References(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position, includeDeclaration bool) ([]protocol.Location, error) {
references, err := references(ctx, snapshot, fh, pp, includeDeclaration)
if err != nil {
return nil, err
@@ -60,7 +61,7 @@
// references returns a list of all references (sorted with
// definitions before uses) to the object denoted by the identifier at
// the given file/position, searching the entire workspace.
-func references(ctx context.Context, snapshot Snapshot, f file.Handle, pp protocol.Position, includeDeclaration bool) ([]reference, error) {
+func references(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, includeDeclaration bool) ([]reference, error) {
ctx, done := event.Start(ctx, "source.references")
defer done()
@@ -107,7 +108,7 @@
// declaration of the specified name and uri by searching among the
// import declarations of all packages that directly import the target
// package.
-func packageReferences(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) ([]reference, error) {
+func packageReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) ([]reference, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, err
@@ -207,7 +208,7 @@
}
// ordinaryReferences computes references for all ordinary objects (not package declarations).
-func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI, pp protocol.Position) ([]reference, error) {
+func ordinaryReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, pp protocol.Position) ([]reference, error) {
// Strategy: use the reference information computed by the
// type checker to find the declaration. First type-check this
// package to find the declaration, then type check the
@@ -505,7 +506,7 @@
// The scope is expanded by a sequence of calls (not concurrent) to addRdeps.
//
// recv is the method's effective receiver type, for method-set computations.
-func expandMethodSearch(ctx context.Context, snapshot Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
+func expandMethodSearch(ctx context.Context, snapshot *cache.Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
// Compute the method-set fingerprint used as a key to the global search.
key, hasMethods := methodsets.KeyOf(recv)
if !hasMethods {
@@ -562,7 +563,7 @@
// localReferences traverses syntax and reports each reference to one
// of the target objects, or (if correspond is set) an object that
// corresponds to one of them via interface satisfaction.
-func localReferences(pkg Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error {
+func localReferences(pkg *cache.Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error {
// If we're searching for references to a method optionally
// broaden the search to include references to corresponding
// methods of mutually assignable receiver types.
diff --git a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go
index 1de646c..ddc4a24 100644
index cded852..c042d15 100644
--- a/gopls/internal/lsp/source/snapshot.go
+++ b/gopls/internal/lsp/source/snapshot.go
@@ -5,207 +5,31 @@
-// Snapshot represents the current state for the given view.
-type Snapshot interface {
- // 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(file.Handle) file.Kind
-
- // Options returns the options associated with this snapshot.
- Options() *settings.Options
-
- // A Snapshot is a caching implementation of FileSource whose
- // ReadFile method returns consistent information about the existence
- // and content of each file throughout its lifetime.
- file.Source
-
- // FindFile returns the FileHandle for the given URI, if it is already
- // in the given snapshot.
- // TODO(adonovan): delete this operation; use ReadFile instead.
- FindFile(uri protocol.DocumentURI) file.Handle
-
- // 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 file.Handle, mode parser.Mode) (*ParsedGoFile, error)
-
- // Analyze runs the specified analyzers on the given packages at this snapshot.
- //
- // If the provided tracker is non-nil, it may be used to report progress of
- // the analysis pass.
- Analyze(ctx context.Context, pkgIDs map[PackageID]unit, analyzers []*settings.Analyzer, tracker *progress.Tracker) ([]*Diagnostic, error)
-
- // RunGoCommandDirect runs the given `go` command. Verb, Args, and
- // WorkingDir must be specified.
- //
- // TODO(rfindley): eliminate this from the Snapshot interface.
- RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, 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(context.Context, *imports.Options) error) error
-
- // ModFiles are the go.mod files enclosed in the snapshot's view and known
- // to the snapshot.
- ModFiles() []protocol.DocumentURI
-
- // ParseMod is used to parse go.mod files.
- ParseMod(ctx context.Context, fh file.Handle) (*ParsedModule, error)
-
- // ParseWork is used to parse go.work files.
- ParseWork(ctx context.Context, fh file.Handle) (*ParsedWorkFile, error)
-
- // BuiltinFile returns information about the special builtin package.
- BuiltinFile(ctx context.Context) (*ParsedGoFile, error)
-
- // Symbols returns all symbols in the snapshot.
- //
- // If workspaceOnly is set, this only includes symbols from files in a
- // workspace package. Otherwise, it returns symbols from all loaded packages.
- Symbols(ctx context.Context, workspaceOnly bool) (map[protocol.DocumentURI][]Symbol, error)
-
- // -- package metadata --
-
- // 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)
-
- // WorkspaceMetadata returns a new, unordered slice containing
- // metadata for all ordinary and test packages (but not
- // intermediate test variants) in the workspace.
- //
- // The workspace is the set of modules typically defined by a
- // go.work file. It is not transitively closed: for example,
- // the standard library is not usually part of the workspace
- // even though every module in the workspace depends on it.
- //
- // Operations that must inspect all the dependencies of the
- // workspace packages should instead use AllMetadata.
- WorkspaceMetadata(ctx context.Context) ([]*Metadata, error)
-
- // AllMetadata returns a new unordered array of metadata for
- // all packages known to this snapshot, which includes the
- // packages of all workspace modules plus their transitive
- // import dependencies.
- //
- // It may also contain ad-hoc packages for standalone files.
- // It includes all test variants.
- AllMetadata(ctx context.Context) ([]*Metadata, 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),
- // and secondarily by IsIntermediateTestVariant (false < true).
- // 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 protocol.DocumentURI) ([]*Metadata, error)
-
- // -- package type-checking --
-
- // 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.
- //
- // In general, clients should never need to type-checked
- // syntax for an intermediate test variant (ITV) package.
- // Callers should apply RemoveIntermediateTestVariants (or
- // equivalent) before this method, or any of the potentially
- // type-checking methods below.
- 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[protocol.DocumentURI][]*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)
-
- // IsGoPrivatePath reports whether target is a private import path, as identified
- // by the GOPRIVATE environment variable.
- IsGoPrivatePath(path string) bool
-
- // Folder returns the folder with which this view was created.
- Folder() protocol.DocumentURI
-
- // GoVersionString returns the go version string configured for this view.
- // Unlike [GoVersion], this encodes the minor version and commit hash information.
- GoVersionString() string
-}
-
// NarrowestMetadataForFile returns metadata for the narrowest package
// (the one with the fewest files) that encloses the specified file.
// The result may be a test variant, but never an intermediate test variant.
-func NarrowestMetadataForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (*Metadata, error) {
+func NarrowestMetadataForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*Metadata, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
if len(metas) == 0 {
return nil, fmt.Errorf("no package metadata for file %s", uri)
}
return metas[0], nil
}
-type XrefIndex interface {
- Lookup(targets map[PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location)
-}
-
// NarrowestPackageForFile is a convenience function that selects the narrowest
// non-ITV package to which this file belongs, type-checks it in the requested
// mode (full or workspace), and returns it, along with the parse tree of that
@@ -221,7 +45,7 @@
//
// Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
// tree, or snapshot.MetadataForFile if you only need metadata.
-func NarrowestPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (Package, *ParsedGoFile, error) {
+func NarrowestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
return selectPackageForFile(ctx, snapshot, uri, func(metas []*Metadata) *Metadata { return metas[0] })
}
@@ -239,16 +63,16 @@
//
// Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
// tree, or snapshot.MetadataForFile if you only need metadata.
-func WidestPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI) (Package, *ParsedGoFile, error) {
+func WidestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
return selectPackageForFile(ctx, snapshot, uri, func(metas []*Metadata) *Metadata { return metas[len(metas)-1] })
}
-func selectPackageForFile(ctx context.Context, snapshot Snapshot, uri protocol.DocumentURI, selector func([]*Metadata) *Metadata) (Package, *ParsedGoFile, error) {
+func selectPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, selector func([]*Metadata) *Metadata) (*cache.Package, *ParsedGoFile, error) {
metas, err := snapshot.MetadataForFile(ctx, uri)
if err != nil {
return nil, nil, err
}
- RemoveIntermediateTestVariants(&metas)
+ metadata.RemoveIntermediateTestVariants(&metas)
if len(metas) == 0 {
return nil, nil, fmt.Errorf("no package metadata for file %s", uri)
}
@@ -265,34 +89,6 @@
@@ -305,43 +101,6 @@
@@ -355,98 +114,4 @@
index 72738a4..1b5f9b7 100644
--- a/gopls/internal/lsp/source/stub.go
+++ b/gopls/internal/lsp/source/stub.go
@@ -21,6 +21,7 @@
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods"
+ "golang.org/x/tools/gopls/internal/lsp/cache"
"golang.org/x/tools/gopls/internal/lsp/cache/metadata"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
@@ -32,7 +33,7 @@
// stubSuggestedFixFunc returns a suggested fix to declare the missing
// methods of the concrete type that is assigned to an interface type
// at the cursor position.
-func stubSuggestedFixFunc(ctx context.Context, snapshot Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
+func stubSuggestedFixFunc(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range) ([]protocol.TextDocumentEdit, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, fmt.Errorf("GetTypedFile: %w", err)
@@ -54,7 +55,7 @@
index c31367b..a6af6f5 100644
@@ -410,23 +411,6 @@
To view, visit change 545315. To unsubscribe, or for help writing mail filters, visit settings.