зеркало из 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
|
// 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
|
// to each data source to select the best result for each entry in the
|
||||||
// missing map.
|
// 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)
|
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
|
var mu sync.Mutex
|
||||||
found := make(map[string][]pkgDistance)
|
found := make(map[string][]pkgDistance)
|
||||||
callback := &scanCallback{
|
callback := &scanCallback{
|
||||||
|
@ -121,5 +121,9 @@ func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename strin
|
||||||
if err := g.Wait(); err != nil {
|
if err := g.Wait(); err != nil {
|
||||||
return nil, err
|
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
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче