зеркало из https://github.com/golang/tools.git
internal/imports: test Source for go mod cache
This CL provides an implementation of the Source interface to use an index to the go module cache to satisfy imports. There is also a test. Change-Id: Ic931cb132fcf7253add7fc7faadd89726ee65567 Reviewed-on: https://go-review.googlesource.com/c/tools/+/627235 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Родитель
9387a3910c
Коммит
a2874818ed
|
@ -59,5 +59,5 @@ type Source interface {
|
|||
// candidates satisfy all missing references for that package name. It is up
|
||||
// to each data source to select the best result for each entry in the
|
||||
// missing map.
|
||||
ResolveReferences(ctx context.Context, filename string, missing References) (map[PackageName]*Result, error)
|
||||
ResolveReferences(ctx context.Context, filename string, missing References) ([]*Result, error)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ func (s *ProcessEnvSource) LoadPackageNames(ctx context.Context, srcDir string,
|
|||
return r.loadPackageNames(unknown, srcDir)
|
||||
}
|
||||
|
||||
func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename string, refs map[string]map[string]bool) (map[string]*Result, error) {
|
||||
func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename string, refs map[string]map[string]bool) ([]*Result, error) {
|
||||
var mu sync.Mutex
|
||||
found := make(map[string][]pkgDistance)
|
||||
callback := &scanCallback{
|
||||
|
@ -121,5 +121,9 @@ func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename strin
|
|||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
var ans []*Result
|
||||
for _, x := range results {
|
||||
ans = append(ans, x)
|
||||
}
|
||||
return ans, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2024 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 imports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/modindex"
|
||||
)
|
||||
|
||||
// This code is here rather than in the modindex package
|
||||
// to avoid import loops
|
||||
|
||||
// implements Source using modindex, so only for module cache.
|
||||
//
|
||||
// this is perhaps over-engineered. A new Index is read at first use.
|
||||
// And then Update is called after every 15 minutes, and a new Index
|
||||
// is read if the index changed. It is not clear the Mutex is needed.
|
||||
type IndexSource struct {
|
||||
modcachedir string
|
||||
mutex sync.Mutex
|
||||
ix *modindex.Index
|
||||
expires time.Time
|
||||
}
|
||||
|
||||
// create a new Source. Called from NewView in cache/session.go.
|
||||
func NewIndexSource(cachedir string) *IndexSource {
|
||||
return &IndexSource{modcachedir: cachedir}
|
||||
}
|
||||
|
||||
func (s *IndexSource) LoadPackageNames(ctx context.Context, srcDir string, paths []ImportPath) (map[ImportPath]PackageName, error) {
|
||||
/// This is used by goimports to resolve the package names of imports of the
|
||||
// current package, which is irrelevant for the module cache.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *IndexSource) ResolveReferences(ctx context.Context, filename string, missing References) ([]*Result, error) {
|
||||
if err := s.maybeReadIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cs []modindex.Candidate
|
||||
for pkg, nms := range missing {
|
||||
for nm := range nms {
|
||||
x := s.ix.Lookup(pkg, nm, false)
|
||||
cs = append(cs, x...)
|
||||
}
|
||||
}
|
||||
found := make(map[string]*Result)
|
||||
for _, c := range cs {
|
||||
var x *Result
|
||||
if x = found[c.ImportPath]; x == nil {
|
||||
x = &Result{
|
||||
Import: &ImportInfo{
|
||||
ImportPath: c.ImportPath,
|
||||
Name: "",
|
||||
},
|
||||
Package: &PackageInfo{
|
||||
Name: c.PkgName,
|
||||
Exports: make(map[string]bool),
|
||||
},
|
||||
}
|
||||
found[c.ImportPath] = x
|
||||
}
|
||||
x.Package.Exports[c.Name] = true
|
||||
}
|
||||
var ans []*Result
|
||||
for _, x := range found {
|
||||
ans = append(ans, x)
|
||||
}
|
||||
return ans, nil
|
||||
}
|
||||
|
||||
func (s *IndexSource) maybeReadIndex() error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
var readIndex bool
|
||||
if time.Now().After(s.expires) {
|
||||
ok, err := modindex.Update(s.modcachedir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
readIndex = true
|
||||
}
|
||||
}
|
||||
|
||||
if readIndex || s.ix == nil {
|
||||
ix, err := modindex.ReadIndex(s.modcachedir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.ix = ix
|
||||
// for now refresh every 15 minutes
|
||||
s.expires = time.Now().Add(time.Minute * 15)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/tools/internal/imports"
|
||||
"golang.org/x/tools/internal/modindex"
|
||||
)
|
||||
|
||||
// There are two cached packages, both resolving foo.Foo,
|
||||
// but only one resolving foo.Bar
|
||||
var (
|
||||
foo = tpkg{
|
||||
repo: "foo.com",
|
||||
dir: "foo@v1.0.0",
|
||||
syms: []string{"Foo"},
|
||||
}
|
||||
foobar = tpkg{
|
||||
repo: "bar.com",
|
||||
dir: "foo@v1.0.0",
|
||||
syms: []string{"Foo", "Bar"},
|
||||
}
|
||||
|
||||
fx = `package main
|
||||
var _ = foo.Foo
|
||||
var _ = foo.Bar
|
||||
`
|
||||
)
|
||||
|
||||
type tpkg struct {
|
||||
// all packages are named foo
|
||||
repo string // e.g. foo.com
|
||||
dir string // e.g., foo@v1.0.0
|
||||
syms []string // exported syms
|
||||
}
|
||||
|
||||
func newpkgs(cachedir string, pks ...*tpkg) error {
|
||||
for _, p := range pks {
|
||||
fname := filepath.Join(cachedir, p.repo, p.dir, "foo.go")
|
||||
if err := os.MkdirAll(filepath.Dir(fname), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
fd, err := os.Create(fname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(fd, "package foo\n")
|
||||
for _, s := range p.syms {
|
||||
fmt.Fprintf(fd, "func %s() {}\n", s)
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
|
||||
dirs := testDirs(t)
|
||||
if err := newpkgs(dirs.cachedir, &foo, &foobar); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source := imports.NewIndexSource(dirs.cachedir)
|
||||
ctx := context.Background()
|
||||
fixes, err := imports.FixImports(ctx, "tfile.go", []byte(fx), "unused", nil, source)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
opts := imports.Options{}
|
||||
// ApplyFixes needs a non-nil opts
|
||||
got, err := imports.ApplyFixes(fixes, "tfile.go", []byte(fx), &opts, 0)
|
||||
|
||||
fxwant := "package main\n\nimport \"bar.com/foo\"\n\nvar _ = foo.Foo\nvar _ = foo.Bar\n"
|
||||
if diff := cmp.Diff(string(got), fxwant); diff != "" {
|
||||
t.Errorf("FixImports got\n%q, wanted\n%q\ndiff is\n%s", string(got), fxwant, diff)
|
||||
}
|
||||
}
|
||||
|
||||
type dirs struct {
|
||||
tmpdir string
|
||||
cachedir string
|
||||
rootdir string // goroot if we need it, which we don't
|
||||
}
|
||||
|
||||
func testDirs(t *testing.T) dirs {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
modindex.IndexDir = func() (string, error) { return dir, nil }
|
||||
x := dirs{
|
||||
tmpdir: dir,
|
||||
cachedir: filepath.Join(dir, "pkg", "mod"),
|
||||
rootdir: filepath.Join(dir, "root"),
|
||||
}
|
||||
if err := os.MkdirAll(x.cachedir, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.MkdirAll(x.rootdir, 0755)
|
||||
return x
|
||||
}
|
Загрузка…
Ссылка в новой задаче