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>
This commit is contained in:
Alan Donovan 2022-11-18 12:03:11 -05:00
parent 2592a854ec
commit 85bf7a8fb4
10 changed files with 128 additions and 169 deletions

View File

@ -46,7 +46,7 @@ type packageHandle struct {
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 @@ func (s *snapshot) buildPackageHandle(ctx context.Context, id PackageID, mode so
// 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 @@ func (s *snapshot) workspaceParseMode(id PackageID) source.ParseMode {
// 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 @@ func (ph *packageHandle) cached() (*pkg, error) {
// 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 @@ func typeCheckImpl(ctx context.Context, snapshot *snapshot, goFiles, compiledGoF
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 @@ func (s *snapshot) depsErrors(ctx context.Context, pkg *pkg) ([]*source.Diagnost
// 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

View File

@ -15,7 +15,7 @@ import (
// 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 @@ type metadataGraph struct {
// 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

View File

@ -157,7 +157,7 @@ func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...loadSc
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 @@ func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...loadSc
//
// 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 @@ func makeWorkspaceDir(ctx context.Context, workspace *workspace, fs source.FileS
// 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 @@ func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Con
}
// 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 @@ func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Con
TypesSizes: pkg.TypesSizes,
Config: cfg,
Module: pkg.Module,
depsErrors: packagesinternal.GetDepsErrors(pkg),
DepsErrors: packagesinternal.GetDepsErrors(pkg),
}
updates[id] = m
@ -606,7 +606,7 @@ func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Con
// 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 @@ func containsPackageLocked(s *snapshot, m *Metadata) bool {
// 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 @@ func containsOpenFileLocked(s *snapshot, m *Metadata) bool {
// 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 @@ func computeWorkspacePackagesLocked(s *snapshot, meta *metadataGraph) map[Packag
// 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...) {

View File

@ -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
}

View File

@ -18,9 +18,17 @@ import (
"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

View File

@ -1128,12 +1128,12 @@ func (s *snapshot) Symbols(ctx context.Context) map[span.URI][]source.Symbol {
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 @@ func (s *snapshot) KnownPackages(ctx context.Context) ([]source.Package, error)
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 @@ func (s *snapshot) AllValidMetadata(ctx context.Context) ([]source.Metadata, err
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 @@ func moduleForURI(modFiles map[span.URI]struct{}, uri span.URI) span.URI {
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 @@ func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileC
// 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]

View File

@ -74,7 +74,7 @@ func Format(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.T
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
}

View File

@ -87,27 +87,27 @@ func PrepareRename(ctx context.Context, snapshot Snapshot, f FileHandle, pp prot
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 @@ func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position,
// 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 @@ func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position,
// 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 @@ func renamePackage(ctx context.Context, s Snapshot, modulePath, oldPath PackageP
// 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 @@ func renamePackage(ctx context.Context, s Snapshot, modulePath, oldPath PackageP
// 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 @@ func (s seenPackageRename) add(uri span.URI, path PackagePath) bool {
// 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 @@ func renamePackageClause(ctx context.Context, m Metadata, s Snapshot, newName Pa
// 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 @@ func renameImports(ctx context.Context, s Snapshot, m Metadata, newPath ImportPa
}
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 @@ func renameImports(ctx context.Context, s Snapshot, m Metadata, newPath ImportPa
// 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
}

View File

@ -28,6 +28,7 @@ import (
"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 @@ type Snapshot interface {
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 @@ type Snapshot interface {
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 @@ type TidiedModule struct {
}
// 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")

View File

@ -88,17 +88,17 @@ type matcherFunc func(chunks []string) (int, float64)
//
// 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 @@ func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher match
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 @@ func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher match
}
// 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 @@ func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher match
// 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 @@ func dynamicSymbolMatch(space []string, name string, pkg Metadata, matcher match
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 @@ func convertFilterToRegexp(filter string) *regexp.Regexp {
// symbolFile holds symbol information for a single file.
type symbolFile struct {
uri span.URI
md Metadata
md *Metadata
syms []Symbol
}
@ -526,7 +526,7 @@ func matchFile(store *symbolStore, symbolizer symbolizer, matcher matcherFunc, r
kind: sym.Kind,
uri: i.uri,
rng: sym.Range,
container: string(i.md.PackagePath()),
container: string(i.md.PkgPath),
}
store.store(si)
}