gopls/internal/lsp/source: eliminate Metadata interface
This change merges the source.Metadata interface with
its sole implementation, cache.Metadata.
One possible concern: the struct cannot have private
fields used only by the cache-package logic that
constructs these structs. We are ok with that.
Change-Id: I93c112f92dc812bd0da07d36e7244d5d77978312
Reviewed-on: https://go-review.googlesource.com/c/tools/+/452035
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index 1f3622a..a22e285 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -46,7 +46,7 @@
promise *memoize.Promise // [typeCheckResult]
// m is the metadata associated with the package.
- m *Metadata
+ m *source.Metadata
// key is the hashed key for the package.
//
@@ -184,7 +184,7 @@
// readGoFiles reads the content of Metadata.GoFiles and
// Metadata.CompiledGoFiles, in parallel.
-func readGoFiles(ctx context.Context, s *snapshot, m *Metadata) (goFiles, compiledGoFiles []source.FileHandle, err error) {
+func readGoFiles(ctx context.Context, s *snapshot, m *source.Metadata) (goFiles, compiledGoFiles []source.FileHandle, err error) {
var group errgroup.Group
getFileHandles := func(files []span.URI) []source.FileHandle {
fhs := make([]source.FileHandle, len(files))
@@ -221,7 +221,7 @@
// computePackageKey returns a key representing the act of type checking
// a package named id containing the specified files, metadata, and
// combined dependency hash.
-func computePackageKey(id PackageID, files []source.FileHandle, m *Metadata, depsKey source.Hash, mode source.ParseMode, experimentalKey bool) packageHandleKey {
+func computePackageKey(id PackageID, files []source.FileHandle, m *source.Metadata, depsKey source.Hash, mode source.ParseMode, experimentalKey bool) packageHandleKey {
// TODO(adonovan): opt: no need to materalize the bytes; hash them directly.
// Also, use field separators to avoid spurious collisions.
b := bytes.NewBuffer(nil)
@@ -304,7 +304,7 @@
// typeCheckImpl type checks the parsed source files in compiledGoFiles.
// (The resulting pkg also holds the parsed but not type-checked goFiles.)
// deps holds the future results of type-checking the direct dependencies.
-func typeCheckImpl(ctx context.Context, snapshot *snapshot, goFiles, compiledGoFiles []source.FileHandle, m *Metadata, mode source.ParseMode, deps map[PackageID]*packageHandle) (*pkg, error) {
+func typeCheckImpl(ctx context.Context, snapshot *snapshot, goFiles, compiledGoFiles []source.FileHandle, m *source.Metadata, mode source.ParseMode, deps map[PackageID]*packageHandle) (*pkg, error) {
// Start type checking of direct dependencies,
// in parallel and asynchronously.
// As the type checker imports each of these
@@ -439,7 +439,7 @@
var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
-func doTypeCheck(ctx context.Context, snapshot *snapshot, goFiles, compiledGoFiles []source.FileHandle, m *Metadata, mode source.ParseMode, deps map[PackageID]*packageHandle, astFilter *unexportedFilter) (*pkg, error) {
+func doTypeCheck(ctx context.Context, snapshot *snapshot, goFiles, compiledGoFiles []source.FileHandle, m *source.Metadata, mode source.ParseMode, deps map[PackageID]*packageHandle, astFilter *unexportedFilter) (*pkg, error) {
ctx, done := event.Start(ctx, "cache.typeCheck", tag.Package.Of(string(m.ID)))
defer done()
@@ -628,7 +628,7 @@
// Select packages that can't be found, and were imported in non-workspace packages.
// Workspace packages already show their own errors.
var relevantErrors []*packagesinternal.PackageError
- for _, depsError := range pkg.m.depsErrors {
+ for _, depsError := range pkg.m.DepsErrors {
// Up to Go 1.15, the missing package was included in the stack, which
// was presumably a bug. We want the next one up.
directImporterIdx := len(depsError.ImportStack) - 1
diff --git a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.go
index be5a061..709f1ab 100644
--- a/gopls/internal/lsp/cache/graph.go
+++ b/gopls/internal/lsp/cache/graph.go
@@ -15,7 +15,7 @@
// graph of Go packages, as obtained from go/packages.
type metadataGraph struct {
// metadata maps package IDs to their associated metadata.
- metadata map[PackageID]*Metadata
+ metadata map[PackageID]*source.Metadata
// importedBy maps package IDs to the list of packages that import them.
importedBy map[PackageID][]PackageID
@@ -27,12 +27,12 @@
// Clone creates a new metadataGraph, applying the given updates to the
// receiver.
-func (g *metadataGraph) Clone(updates map[PackageID]*Metadata) *metadataGraph {
+func (g *metadataGraph) Clone(updates map[PackageID]*source.Metadata) *metadataGraph {
if len(updates) == 0 {
// Optimization: since the graph is immutable, we can return the receiver.
return g
}
- result := &metadataGraph{metadata: make(map[PackageID]*Metadata, len(g.metadata))}
+ result := &metadataGraph{metadata: make(map[PackageID]*source.Metadata, len(g.metadata))}
// Copy metadata.
for id, m := range g.metadata {
result.metadata[id] = m
diff --git a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go
index 46e2e89..5f31958 100644
--- a/gopls/internal/lsp/cache/load.go
+++ b/gopls/internal/lsp/cache/load.go
@@ -157,7 +157,7 @@
moduleErrs := make(map[string][]packages.Error) // module path -> errors
filterer := buildFilterer(s.view.rootURI.Filename(), s.view.gomodcache, s.view.Options())
- newMetadata := make(map[PackageID]*Metadata)
+ newMetadata := make(map[PackageID]*source.Metadata)
for _, pkg := range pkgs {
// The Go command returns synthetic list results for module queries that
// encountered module errors.
@@ -222,7 +222,7 @@
//
// TODO(rfindley): perform a sanity check that metadata matches here. If not,
// we have an invalidation bug elsewhere.
- updates := make(map[PackageID]*Metadata)
+ updates := make(map[PackageID]*source.Metadata)
var updatedIDs []PackageID
for _, m := range newMetadata {
if existing := s.meta.metadata[m.ID]; existing == nil {
@@ -475,7 +475,7 @@
// buildMetadata populates the updates map with metadata updates to
// apply, based on the given pkg. It recurs through pkg.Imports to ensure that
// metadata exists for all dependencies.
-func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Config, query []string, updates map[PackageID]*Metadata, path []PackageID) error {
+func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Config, query []string, updates map[PackageID]*source.Metadata, path []PackageID) error {
// Allow for multiple ad-hoc packages in the workspace (see #47584).
pkgPath := PackagePath(pkg.PkgPath)
id := PackageID(pkg.ID)
@@ -507,7 +507,7 @@
}
// Recreate the metadata rather than reusing it to avoid locking.
- m := &Metadata{
+ m := &source.Metadata{
ID: id,
PkgPath: pkgPath,
Name: PackageName(pkg.Name),
@@ -515,7 +515,7 @@
TypesSizes: pkg.TypesSizes,
Config: cfg,
Module: pkg.Module,
- depsErrors: packagesinternal.GetDepsErrors(pkg),
+ DepsErrors: packagesinternal.GetDepsErrors(pkg),
}
updates[id] = m
@@ -606,7 +606,7 @@
// snapshot s.
//
// s.mu must be held while calling this function.
-func containsPackageLocked(s *snapshot, m *Metadata) bool {
+func containsPackageLocked(s *snapshot, m *source.Metadata) bool {
// In legacy workspace mode, or if a package does not have an associated
// module, a package is considered inside the workspace if any of its files
// are under the workspace root (and not excluded).
@@ -647,7 +647,7 @@
// the snapshot s.
//
// s.mu must be held while calling this function.
-func containsOpenFileLocked(s *snapshot, m *Metadata) bool {
+func containsOpenFileLocked(s *snapshot, m *source.Metadata) bool {
uris := map[span.URI]struct{}{}
for _, uri := range m.CompiledGoFiles {
uris[uri] = struct{}{}
@@ -668,7 +668,7 @@
// workspace of the snapshot s.
//
// s.mu must be held while calling this function.
-func containsFileInWorkspaceLocked(s *snapshot, m *Metadata) bool {
+func containsFileInWorkspaceLocked(s *snapshot, m *source.Metadata) bool {
uris := map[span.URI]struct{}{}
for _, uri := range m.CompiledGoFiles {
uris[uri] = struct{}{}
@@ -738,7 +738,7 @@
// function returns false.
//
// If m is not a command-line-arguments package, this is trivially true.
-func allFilesHaveRealPackages(g *metadataGraph, m *Metadata) bool {
+func allFilesHaveRealPackages(g *metadataGraph, m *source.Metadata) bool {
n := len(m.CompiledGoFiles)
checkURIs:
for _, uri := range append(m.CompiledGoFiles[0:n:n], m.GoFiles...) {
diff --git a/gopls/internal/lsp/cache/metadata.go b/gopls/internal/lsp/cache/metadata.go
deleted file mode 100644
index 135623f..0000000
--- a/gopls/internal/lsp/cache/metadata.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2021 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 (
- "go/types"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/gopls/internal/lsp/source"
- "golang.org/x/tools/gopls/internal/span"
- "golang.org/x/tools/internal/packagesinternal"
-)
-
-type (
- PackageID = source.PackageID
- PackagePath = source.PackagePath
- PackageName = source.PackageName
- ImportPath = source.ImportPath
-)
-
-// Metadata holds package Metadata extracted from a call to packages.Load.
-type Metadata struct {
- ID PackageID
- PkgPath PackagePath
- Name PackageName
- GoFiles []span.URI
- CompiledGoFiles []span.URI
- ForTest PackagePath // package path under test, or ""
- TypesSizes types.Sizes
- Errors []packages.Error
- DepsByImpPath map[ImportPath]PackageID // may contain dups; empty ID => missing
- DepsByPkgPath map[PackagePath]PackageID // values are unique and non-empty
- Module *packages.Module
- depsErrors []*packagesinternal.PackageError
-
- // Config is the *packages.Config associated with the loaded package.
- Config *packages.Config
-}
-
-// PackageID implements the source.Metadata interface.
-func (m *Metadata) PackageID() PackageID { return m.ID }
-
-// Name implements the source.Metadata interface.
-func (m *Metadata) PackageName() PackageName { return m.Name }
-
-// PkgPath implements the source.Metadata interface.
-func (m *Metadata) PackagePath() PackagePath { return m.PkgPath }
-
-// IsIntermediateTestVariant reports whether the given package is an
-// intermediate test variant, e.g. "net/http [net/url.test]".
-//
-// Such test variants arise when an x_test package (in this case net/url_test)
-// imports a package (in this case net/http) that itself imports the the
-// non-x_test package (in this case net/url).
-//
-// This is done so that the forward transitive closure of net/url_test has
-// only one package for the "net/url" import.
-// The intermediate test variant exists to hold the test variant import:
-//
-// net/url_test [net/url.test]
-//
-// | "net/http" -> net/http [net/url.test]
-// | "net/url" -> net/url [net/url.test]
-// | ...
-//
-// net/http [net/url.test]
-//
-// | "net/url" -> net/url [net/url.test]
-// | ...
-//
-// This restriction propagates throughout the import graph of net/http: for
-// every package imported by net/http that imports net/url, there must be an
-// intermediate test variant that instead imports "net/url [net/url.test]".
-//
-// As one can see from the example of net/url and net/http, intermediate test
-// variants can result in many additional packages that are essentially (but
-// not quite) identical. For this reason, we filter these variants wherever
-// possible.
-func (m *Metadata) IsIntermediateTestVariant() bool {
- return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath
-}
-
-// ModuleInfo implements the source.Metadata interface.
-func (m *Metadata) ModuleInfo() *packages.Module {
- return m.Module
-}
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index 6e1233a..5c43a82 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -18,9 +18,17 @@
"golang.org/x/tools/internal/memoize"
)
+// Convenient local aliases for typed strings.
+type (
+ PackageID = source.PackageID
+ PackagePath = source.PackagePath
+ PackageName = source.PackageName
+ ImportPath = source.ImportPath
+)
+
// pkg contains the type information needed by the source package.
type pkg struct {
- m *Metadata
+ m *source.Metadata
mode source.ParseMode
fset *token.FileSet // for now, same as the snapshot's FileSet
goFiles []*source.ParsedGoFile
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 281c5d9..ae5fe28 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -1128,12 +1128,12 @@
return result
}
-func (s *snapshot) MetadataForFile(ctx context.Context, uri span.URI) ([]source.Metadata, error) {
+func (s *snapshot) MetadataForFile(ctx context.Context, uri span.URI) ([]*source.Metadata, error) {
knownIDs, err := s.getOrLoadIDsForURI(ctx, uri)
if err != nil {
return nil, err
}
- var mds []source.Metadata
+ var mds []*source.Metadata
for _, id := range knownIDs {
md := s.getMetadata(id)
// TODO(rfindley): knownIDs and metadata should be in sync, but existing
@@ -1168,7 +1168,7 @@
return pkgs, nil
}
-func (s *snapshot) AllValidMetadata(ctx context.Context) ([]source.Metadata, error) {
+func (s *snapshot) AllValidMetadata(ctx context.Context) ([]*source.Metadata, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
}
@@ -1176,7 +1176,7 @@
s.mu.Lock()
defer s.mu.Unlock()
- var meta []source.Metadata
+ var meta []*source.Metadata
for _, m := range s.meta.metadata {
meta = append(meta, m)
}
@@ -1233,7 +1233,7 @@
return match
}
-func (s *snapshot) getMetadata(id PackageID) *Metadata {
+func (s *snapshot) getMetadata(id PackageID) *source.Metadata {
s.mu.Lock()
defer s.mu.Unlock()
@@ -1922,7 +1922,7 @@
// Compute which metadata updates are required. We only need to invalidate
// packages directly containing the affected file, and only if it changed in
// a relevant way.
- metadataUpdates := make(map[PackageID]*Metadata)
+ metadataUpdates := make(map[PackageID]*source.Metadata)
for k, v := range s.meta.metadata {
invalidateMetadata := idsToInvalidate[k]
diff --git a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/format.go
index a83c9e4..2605cbb 100644
--- a/gopls/internal/lsp/source/format.go
+++ b/gopls/internal/lsp/source/format.go
@@ -74,7 +74,7 @@
var langVersion, modulePath string
mds, err := snapshot.MetadataForFile(ctx, fh.URI())
if err == nil && len(mds) > 0 {
- if mi := mds[0].ModuleInfo(); mi != nil {
+ if mi := mds[0].Module; mi != nil {
langVersion = mi.GoVersion
modulePath = mi.Path
}
diff --git a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go
index 9586d84..51555b0 100644
--- a/gopls/internal/lsp/source/rename.go
+++ b/gopls/internal/lsp/source/rename.go
@@ -87,27 +87,27 @@
meta := fileMeta[0]
- if meta.PackageName() == "main" {
+ if meta.Name == "main" {
err := errors.New("can't rename package \"main\"")
return nil, err, err
}
- if strings.HasSuffix(string(meta.PackageName()), "_test") {
+ if strings.HasSuffix(string(meta.Name), "_test") {
err := errors.New("can't rename x_test packages")
return nil, err, err
}
- if meta.ModuleInfo() == nil {
- err := fmt.Errorf("can't rename package: missing module information for package %q", meta.PackagePath())
+ if meta.Module == nil {
+ err := fmt.Errorf("can't rename package: missing module information for package %q", meta.PkgPath)
return nil, err, err
}
- if meta.ModuleInfo().Path == string(meta.PackagePath()) {
- err := fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PackagePath(), meta.ModuleInfo().Path)
+ if meta.Module.Path == string(meta.PkgPath) {
+ err := fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PkgPath, meta.Module.Path)
return nil, err, err
}
// TODO(rfindley): we should not need the package here.
- pkg, err := snapshot.WorkspacePackageByID(ctx, meta.PackageID())
+ pkg, err := snapshot.WorkspacePackageByID(ctx, meta.ID)
if err != nil {
err = fmt.Errorf("error building package to rename: %v", err)
return nil, err, err
@@ -200,10 +200,10 @@
// TODO(rfindley): we mix package path and import path here haphazardly.
// Fix this.
meta := fileMeta[0]
- oldPath := meta.PackagePath()
+ oldPath := meta.PkgPath
var modulePath PackagePath
- if mi := meta.ModuleInfo(); mi == nil {
- return nil, true, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PackagePath())
+ if mi := meta.Module; mi == nil {
+ return nil, true, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PkgPath)
} else {
modulePath = PackagePath(mi.Path)
}
@@ -244,7 +244,7 @@
// It updates package clauses and import paths for the renamed package as well
// as any other packages affected by the directory renaming among packages
// described by allMetadata.
-func renamePackage(ctx context.Context, s Snapshot, modulePath, oldPath PackagePath, newName PackageName, allMetadata []Metadata) (map[span.URI][]protocol.TextEdit, error) {
+func renamePackage(ctx context.Context, s Snapshot, modulePath, oldPath PackagePath, newName PackageName, allMetadata []*Metadata) (map[span.URI][]protocol.TextEdit, error) {
if modulePath == oldPath {
return nil, fmt.Errorf("cannot rename package: module path %q is the same as the package path, so renaming the package directory would have no effect", modulePath)
}
@@ -259,7 +259,7 @@
// Special case: x_test packages for the renamed package will not have the
// package path as as a dir prefix, but still need their package clauses
// renamed.
- if m.PackagePath() == oldPath+"_test" {
+ if m.PkgPath == oldPath+"_test" {
newTestName := newName + "_test"
if err := renamePackageClause(ctx, m, s, newTestName, seen, edits); err != nil {
@@ -271,24 +271,24 @@
// Subtle: check this condition before checking for valid module info
// below, because we should not fail this operation if unrelated packages
// lack module info.
- if !strings.HasPrefix(string(m.PackagePath())+"/", string(oldPath)+"/") {
+ if !strings.HasPrefix(string(m.PkgPath)+"/", string(oldPath)+"/") {
continue // not affected by the package renaming
}
- if m.ModuleInfo() == nil {
- return nil, fmt.Errorf("cannot rename package: missing module information for package %q", m.PackagePath())
+ if m.Module == nil {
+ return nil, fmt.Errorf("cannot rename package: missing module information for package %q", m.PkgPath)
}
- if modulePath != PackagePath(m.ModuleInfo().Path) {
+ if modulePath != PackagePath(m.Module.Path) {
continue // don't edit imports if nested package and renaming package have different module paths
}
// Renaming a package consists of changing its import path and package name.
- suffix := strings.TrimPrefix(string(m.PackagePath()), string(oldPath))
+ suffix := strings.TrimPrefix(string(m.PkgPath), string(oldPath))
newPath := newPathPrefix + suffix
- pkgName := m.PackageName()
- if m.PackagePath() == PackagePath(oldPath) {
+ pkgName := m.Name
+ if m.PkgPath == PackagePath(oldPath) {
pkgName = newName
if err := renamePackageClause(ctx, m, s, newName, seen, edits); err != nil {
@@ -336,15 +336,15 @@
// package clause has already been updated, to prevent duplicate edits.
//
// Edits are written into the edits map.
-func renamePackageClause(ctx context.Context, m Metadata, s Snapshot, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error {
- pkg, err := s.WorkspacePackageByID(ctx, m.PackageID())
+func renamePackageClause(ctx context.Context, m *Metadata, s Snapshot, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error {
+ pkg, err := s.WorkspacePackageByID(ctx, m.ID)
if err != nil {
return err
}
// Rename internal references to the package in the renaming package.
for _, f := range pkg.CompiledGoFiles() {
- if seen.add(f.URI, m.PackagePath()) {
+ if seen.add(f.URI, m.PkgPath) {
continue
}
@@ -370,11 +370,11 @@
// newPath and name newName.
//
// Edits are written into the edits map.
-func renameImports(ctx context.Context, s Snapshot, m Metadata, newPath ImportPath, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error {
+func renameImports(ctx context.Context, s Snapshot, m *Metadata, newPath ImportPath, newName PackageName, seen seenPackageRename, edits map[span.URI][]protocol.TextEdit) error {
// TODO(rfindley): we should get reverse dependencies as metadata first,
// rather then building the package immediately. We don't need reverse
// dependencies if they are intermediate test variants.
- rdeps, err := s.GetReverseDependencies(ctx, m.PackageID())
+ rdeps, err := s.GetReverseDependencies(ctx, m.ID)
if err != nil {
return err
}
@@ -394,13 +394,13 @@
}
for _, f := range dep.CompiledGoFiles() {
- if seen.add(f.URI, m.PackagePath()) {
+ if seen.add(f.URI, m.PkgPath) {
continue
}
for _, imp := range f.File.Imports {
// TODO(adonovan): what if RHS has "vendor/" prefix?
- if UnquoteImportPath(imp) != ImportPath(m.PackagePath()) {
+ if UnquoteImportPath(imp) != ImportPath(m.PkgPath) {
continue // not the import we're looking for
}
@@ -418,7 +418,7 @@
// If the package name of an import has not changed or if its import
// path already has a local package name, then we don't need to update
// the local package name.
- if newName == m.PackageName() || imp.Name != nil {
+ if newName == m.Name || imp.Name != nil {
continue
}
diff --git a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go
index 4178197..7466484 100644
--- a/gopls/internal/lsp/source/view.go
+++ b/gopls/internal/lsp/source/view.go
@@ -28,6 +28,7 @@
"golang.org/x/tools/internal/event/tag"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/imports"
+ "golang.org/x/tools/internal/packagesinternal"
)
// A GlobalSnapshotID uniquely identifies a snapshot within this process and
@@ -196,7 +197,7 @@
ActivePackages(ctx context.Context) ([]Package, error)
// AllValidMetadata returns all valid metadata loaded for the snapshot.
- AllValidMetadata(ctx context.Context) ([]Metadata, error)
+ AllValidMetadata(ctx context.Context) ([]*Metadata, error)
// WorkspacePackageByID returns the workspace package with id, type checked
// in 'workspace' mode.
@@ -206,7 +207,7 @@
Symbols(ctx context.Context) map[span.URI][]Symbol
// Metadata returns package metadata associated with the given file URI.
- MetadataForFile(ctx context.Context, uri span.URI) ([]Metadata, error)
+ MetadataForFile(ctx context.Context, uri span.URI) ([]*Metadata, error)
// GetCriticalError returns any critical errors in the workspace.
GetCriticalError(ctx context.Context) *CriticalError
@@ -373,18 +374,56 @@
}
// Metadata represents package metadata retrieved from go/packages.
-type Metadata interface {
- // PackageID is the unique package id.
- PackageID() PackageID
+type Metadata struct {
+ ID PackageID
+ PkgPath PackagePath
+ Name PackageName
+ GoFiles []span.URI
+ CompiledGoFiles []span.URI
+ ForTest PackagePath // package path under test, or ""
+ TypesSizes types.Sizes
+ Errors []packages.Error
+ DepsByImpPath map[ImportPath]PackageID // may contain dups; empty ID => missing
+ DepsByPkgPath map[PackagePath]PackageID // values are unique and non-empty
+ Module *packages.Module
+ DepsErrors []*packagesinternal.PackageError
- // PackageName is the package name.
- PackageName() PackageName
+ // Config is the *packages.Config associated with the loaded package.
+ Config *packages.Config
+}
- // PackagePath is the package path.
- PackagePath() PackagePath
-
- // ModuleInfo returns the go/packages module information for the given package.
- ModuleInfo() *packages.Module
+// IsIntermediateTestVariant reports whether the given package is an
+// intermediate test variant, e.g. "net/http [net/url.test]".
+//
+// Such test variants arise when an x_test package (in this case net/url_test)
+// imports a package (in this case net/http) that itself imports the the
+// non-x_test package (in this case net/url).
+//
+// This is done so that the forward transitive closure of net/url_test has
+// only one package for the "net/url" import.
+// The intermediate test variant exists to hold the test variant import:
+//
+// net/url_test [net/url.test]
+//
+// | "net/http" -> net/http [net/url.test]
+// | "net/url" -> net/url [net/url.test]
+// | ...
+//
+// net/http [net/url.test]
+//
+// | "net/url" -> net/url [net/url.test]
+// | ...
+//
+// This restriction propagates throughout the import graph of net/http: for
+// every package imported by net/http that imports net/url, there must be an
+// intermediate test variant that instead imports "net/url [net/url.test]".
+//
+// As one can see from the example of net/url and net/http, intermediate test
+// variants can result in many additional packages that are essentially (but
+// not quite) identical. For this reason, we filter these variants wherever
+// possible.
+func (m *Metadata) IsIntermediateTestVariant() bool {
+ return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath
}
var ErrViewExists = errors.New("view already exists for session")
diff --git a/gopls/internal/lsp/source/workspace_symbol.go b/gopls/internal/lsp/source/workspace_symbol.go
index dfb5f39..dc5abd2 100644
--- a/gopls/internal/lsp/source/workspace_symbol.go
+++ b/gopls/internal/lsp/source/workspace_symbol.go
@@ -88,17 +88,17 @@
//
// The space argument is an empty slice with spare capacity that may be used
// to allocate the result.
-type symbolizer func(space []string, name string, pkg Metadata, m matcherFunc) ([]string, float64)
+type symbolizer func(space []string, name string, pkg *Metadata, m matcherFunc) ([]string, float64)
-func fullyQualifiedSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) {
+func fullyQualifiedSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) {
if _, score := dynamicSymbolMatch(space, name, pkg, matcher); score > 0 {
- return append(space, string(pkg.PackagePath()), ".", name), score
+ return append(space, string(pkg.PkgPath), ".", name), score
}
return nil, 0
}
-func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) {
- if IsCommandLineArguments(pkg.PackageID()) {
+func dynamicSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) {
+ if IsCommandLineArguments(pkg.ID) {
// command-line-arguments packages have a non-sensical package path, so
// just use their package name.
return packageSymbolMatch(space, name, pkg, matcher)
@@ -106,14 +106,14 @@
var score float64
- endsInPkgName := strings.HasSuffix(string(pkg.PackagePath()), string(pkg.PackageName()))
+ endsInPkgName := strings.HasSuffix(string(pkg.PkgPath), string(pkg.Name))
// If the package path does not end in the package name, we need to check the
// package-qualified symbol as an extra pass first.
if !endsInPkgName {
- pkgQualified := append(space, string(pkg.PackageName()), ".", name)
+ pkgQualified := append(space, string(pkg.Name), ".", name)
idx, score := matcher(pkgQualified)
- nameStart := len(pkg.PackageName()) + 1
+ nameStart := len(pkg.Name) + 1
if score > 0 {
// If our match is contained entirely within the unqualified portion,
// just return that.
@@ -126,11 +126,11 @@
}
// Now try matching the fully qualified symbol.
- fullyQualified := append(space, string(pkg.PackagePath()), ".", name)
+ fullyQualified := append(space, string(pkg.PkgPath), ".", name)
idx, score := matcher(fullyQualified)
// As above, check if we matched just the unqualified symbol name.
- nameStart := len(pkg.PackagePath()) + 1
+ nameStart := len(pkg.PkgPath) + 1
if idx >= nameStart {
return append(space, name), score
}
@@ -139,9 +139,9 @@
// initial pass above, so check if we matched just the package-qualified
// name.
if endsInPkgName && idx >= 0 {
- pkgStart := len(pkg.PackagePath()) - len(pkg.PackageName())
+ pkgStart := len(pkg.PkgPath) - len(pkg.Name)
if idx >= pkgStart {
- return append(space, string(pkg.PackageName()), ".", name), score
+ return append(space, string(pkg.Name), ".", name), score
}
}
@@ -150,8 +150,8 @@
return fullyQualified, score * 0.6
}
-func packageSymbolMatch(space []string, name string, pkg Metadata, matcher matcherFunc) ([]string, float64) {
- qualified := append(space, string(pkg.PackageName()), ".", name)
+func packageSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) {
+ qualified := append(space, string(pkg.Name), ".", name)
if _, s := matcher(qualified); s > 0 {
return qualified, s
}
@@ -441,7 +441,7 @@
// symbolFile holds symbol information for a single file.
type symbolFile struct {
uri span.URI
- md Metadata
+ md *Metadata
syms []Symbol
}
@@ -526,7 +526,7 @@
kind: sym.Kind,
uri: i.uri,
rng: sym.Range,
- container: string(i.md.PackagePath()),
+ container: string(i.md.PkgPath),
}
store.store(si)
}