1
0
mirror of https://github.com/golang/go synced 2024-10-01 03:18:33 -06:00

imports: sibling imports must have matching references

When selecting a sibling's import, the unresolved reference must have
been also used otherwise use the normal search to determine the best
possible package to import.

Fixes golang/go#23001

Change-Id: I38a983569991464970ad5921fe7f280dd3e35a2c
Reviewed-on: https://go-review.googlesource.com/82875
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Michael Fraenkel 2017-12-08 09:26:14 -05:00 committed by Brad Fitzpatrick
parent b790d0ba03
commit 8cab8a1319
2 changed files with 63 additions and 4 deletions

View File

@ -78,6 +78,10 @@ type importInfo struct {
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 Imports map[string]importInfo // pkg base name or alias => info
// refs are a set of package references currently satisfied by imports.
// first key: either base package (e.g. "fmt") or renamed package
// second key: referenced package symbol (e.g. "Println")
Refs map[string]map[string]bool
} }
// dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden. // dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden.
@ -93,7 +97,13 @@ func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error)
return nil, err return nil, err
} }
info := &packageInfo{Globals: make(map[string]bool), Imports: make(map[string]importInfo)} info := &packageInfo{
Globals: make(map[string]bool),
Imports: make(map[string]importInfo),
Refs: make(map[string]map[string]bool),
}
visitor := collectReferences(info.Refs)
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
@ -132,10 +142,45 @@ func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error)
} }
info.Imports[name] = impInfo info.Imports[name] = impInfo
} }
ast.Walk(visitor, root)
} }
return info, nil return info, nil
} }
// collectReferences returns a visitor that collects all exported package
// references
func collectReferences(refs map[string]map[string]bool) visitFn {
var visitor visitFn
visitor = func(node ast.Node) ast.Visitor {
if node == nil {
return visitor
}
switch v := node.(type) {
case *ast.SelectorExpr:
xident, ok := v.X.(*ast.Ident)
if !ok {
break
}
if xident.Obj != nil {
// if the parser can resolve it, it's not a package ref
break
}
pkgName := xident.Name
r := refs[pkgName]
if r == nil {
r = make(map[string]bool)
refs[pkgName] = r
}
if ast.IsExported(v.Sel.Name) {
r[v.Sel.Name] = true
}
}
return visitor
}
return visitor
}
func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) { func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) {
// refs are a set of possible package references currently unsatisfied by imports. // refs are a set of possible package references currently unsatisfied by imports.
// first key: either base package (e.g. "fmt") or renamed package // first key: either base package (e.g. "fmt") or renamed package
@ -249,8 +294,13 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
if packageInfo != nil { if packageInfo != nil {
sibling := packageInfo.Imports[pkgName] sibling := packageInfo.Imports[pkgName]
if sibling.Path != "" { if sibling.Path != "" {
results <- result{ipath: sibling.Path, name: sibling.Alias} refs := packageInfo.Refs[pkgName]
return for symbol := range symbols {
if refs[symbol] {
results <- result{ipath: sibling.Path, name: sibling.Alias}
return
}
}
} }
} }
ipath, rename, err := findImport(pkgName, symbols, filename) ipath, rename, err := findImport(pkgName, symbols, filename)

View File

@ -1609,15 +1609,19 @@ func TestSiblingImports(t *testing.T) {
const provide = `package siblingimporttest const provide = `package siblingimporttest
import "local/log" import "local/log"
import "my/bytes"
func LogSomething() { func LogSomething() {
log.Print("Something") log.Print("Something")
bytes.SomeFunc()
} }
` `
// need is the file being tested that needs the import. // need is the file being tested that needs the import.
const need = `package siblingimporttest const need = `package siblingimporttest
var _ = bytes.Buffer{}
func LogSomethingElse() { func LogSomethingElse() {
log.Print("Something else") log.Print("Something else")
} }
@ -1626,7 +1630,12 @@ func LogSomethingElse() {
// want is the expected result file // want is the expected result file
const want = `package siblingimporttest const want = `package siblingimporttest
import "local/log" import (
"bytes"
"local/log"
)
var _ = bytes.Buffer{}
func LogSomethingElse() { func LogSomethingElse() {
log.Print("Something else") log.Print("Something else")