imports: prefer paths imported by sibling files.
Adds an Imports field to packageInfo with the imports used by sibling files, and uses it preferentially if it matches a missing import. Example: if foo/foo.go imports "local/log", it's a reasonable assumption that foo/bar.go will also want "local/log" instead of "log". Change-Id: Ifb504ed5e00ff18459f19d8598cc2c94099ae563 Reviewed-on: https://go-review.googlesource.com/43454 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
e493388965
commit
e011c1062a
|
@ -68,9 +68,16 @@ func importGroup(importPath string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// importInfo is a summary of information about one import.
|
||||||
|
type importInfo struct {
|
||||||
|
Path string // full import path (e.g. "crypto/rand")
|
||||||
|
Alias string // import alias, if present (e.g. "crand")
|
||||||
|
}
|
||||||
|
|
||||||
// packageInfo is a summary of features found in a package.
|
// packageInfo is a summary of features found in a package.
|
||||||
type packageInfo struct {
|
type packageInfo struct {
|
||||||
Globals map[string]bool // symbol => true
|
Globals map[string]bool // symbol => true
|
||||||
|
Imports map[string]importInfo // pkg base name or alias => info
|
||||||
}
|
}
|
||||||
|
|
||||||
// dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden.
|
// dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden.
|
||||||
|
@ -94,7 +101,7 @@ func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := &packageInfo{Globals: make(map[string]bool)}
|
info := &packageInfo{Globals: make(map[string]bool), Imports: make(map[string]importInfo)}
|
||||||
for _, fi := range packageFileInfos {
|
for _, fi := range packageFileInfos {
|
||||||
if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") {
|
if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") {
|
||||||
continue
|
continue
|
||||||
|
@ -123,6 +130,16 @@ func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error)
|
||||||
info.Globals[valueSpec.Names[0].Name] = true
|
info.Globals[valueSpec.Names[0].Name] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, imp := range root.Imports {
|
||||||
|
impInfo := importInfo{Path: strings.Trim(imp.Path.Value, `"`)}
|
||||||
|
name := path.Base(impInfo.Path)
|
||||||
|
if imp.Name != nil {
|
||||||
|
name = strings.Trim(imp.Name.Name, `"`)
|
||||||
|
impInfo.Alias = name
|
||||||
|
}
|
||||||
|
info.Imports[name] = impInfo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
@ -217,6 +234,16 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast path, all references already imported.
|
||||||
|
if len(refs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can assume this will be necessary in all cases now.
|
||||||
|
if !loadedPackageInfo {
|
||||||
|
packageInfo, _ = dirPackageInfo(f.Name.Name, srcDir, filename)
|
||||||
|
}
|
||||||
|
|
||||||
// Search for imports matching potential package references.
|
// Search for imports matching potential package references.
|
||||||
searches := 0
|
searches := 0
|
||||||
type result struct {
|
type result struct {
|
||||||
|
@ -227,6 +254,11 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
|
||||||
results := make(chan result)
|
results := make(chan result)
|
||||||
for pkgName, symbols := range refs {
|
for pkgName, symbols := range refs {
|
||||||
go func(pkgName string, symbols map[string]bool) {
|
go func(pkgName string, symbols map[string]bool) {
|
||||||
|
sibling := packageInfo.Imports[pkgName]
|
||||||
|
if sibling.Path != "" {
|
||||||
|
results <- result{ipath: sibling.Path, name: sibling.Alias}
|
||||||
|
return
|
||||||
|
}
|
||||||
ipath, rename, err := findImport(pkgName, symbols, filename)
|
ipath, rename, err := findImport(pkgName, symbols, filename)
|
||||||
r := result{ipath: ipath, err: err}
|
r := result{ipath: ipath, err: err}
|
||||||
if rename {
|
if rename {
|
||||||
|
|
|
@ -1536,6 +1536,57 @@ func TestGlobalImports(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that sibling files - other files in the same package - can provide an
|
||||||
|
// import that may not be the default one otherwise.
|
||||||
|
func TestSiblingImports(t *testing.T) {
|
||||||
|
|
||||||
|
// provide is the sibling file that provides the desired import.
|
||||||
|
const provide = `package siblingimporttest
|
||||||
|
|
||||||
|
import "local/log"
|
||||||
|
|
||||||
|
func LogSomething() {
|
||||||
|
log.Print("Something")
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// need is the file being tested that needs the import.
|
||||||
|
const need = `package siblingimporttest
|
||||||
|
|
||||||
|
func LogSomethingElse() {
|
||||||
|
log.Print("Something else")
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// want is the expected result file
|
||||||
|
const want = `package siblingimporttest
|
||||||
|
|
||||||
|
import "local/log"
|
||||||
|
|
||||||
|
func LogSomethingElse() {
|
||||||
|
log.Print("Something else")
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const pkg = "siblingimporttest"
|
||||||
|
const siblingFile = pkg + "/needs_import.go"
|
||||||
|
testConfig{
|
||||||
|
gopathFiles: map[string]string{
|
||||||
|
siblingFile: need,
|
||||||
|
pkg + "/provides_import.go": provide,
|
||||||
|
},
|
||||||
|
}.test(t, func(t *goimportTest) {
|
||||||
|
buf, err := Process(
|
||||||
|
t.gopath+"/src/"+siblingFile, []byte(need), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(buf) != want {
|
||||||
|
t.Errorf("wrong output.\ngot:\n%q\nwant:\n%q\n", buf, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func strSet(ss []string) map[string]bool {
|
func strSet(ss []string) map[string]bool {
|
||||||
m := make(map[string]bool)
|
m := make(map[string]bool)
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
|
|
Loading…
Reference in New Issue