go/loader: resolve imports in "created" packages w.r.t. parent dir not cwd
Fixes golang/go#16580
Change-Id: Id891f40659257eac9dbb0d70dae17f725e911f9b
Reviewed-on: https://go-review.googlesource.com/33589
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/loader/loader.go b/go/loader/loader.go
index f0171fc..0d4c0d1 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -17,6 +17,7 @@
"go/token"
"go/types"
"os"
+ "path/filepath"
"sort"
"strings"
"sync"
@@ -132,6 +133,8 @@
// Files are processed first, but typically only one of Files and
// Filenames is provided. The path needn't be globally unique.
//
+// For vendoring purposes, the package's directory is the one that
+// contains the first file.
type PkgSpec struct {
Path string // package path ("" => use package declaration)
Files []*ast.File // ASTs of already-parsed files
@@ -586,9 +589,8 @@
imp.addFiles(info, files, false)
}
- createPkg := func(path string, files []*ast.File, errs []error) {
- // TODO(adonovan): fix: use dirname of files, not cwd.
- info := imp.newPackageInfo(path, conf.Cwd)
+ createPkg := func(path, dir string, files []*ast.File, errs []error) {
+ info := imp.newPackageInfo(path, dir)
for _, err := range errs {
info.appendError(err)
}
@@ -613,14 +615,19 @@
path = "(unnamed)"
}
}
- createPkg(path, files, errs)
+
+ dir := "."
+ if len(files) > 0 && files[0].Pos().IsValid() {
+ dir = filepath.Dir(conf.fset().File(files[0].Pos()).Name())
+ }
+ createPkg(path, dir, files, errs)
}
// Create external test packages.
sort.Sort(byImportPath(xtestPkgs))
for _, bp := range xtestPkgs {
files, errs := imp.conf.parsePackageFiles(bp, 'x')
- createPkg(bp.ImportPath+"_test", files, errs)
+ createPkg(bp.ImportPath+"_test", bp.Dir, files, errs)
}
// -- finishing up (sequential) ----------------------------------------
diff --git a/go/loader/loader_test.go b/go/loader/loader_test.go
index 1f05e18..540aa4c 100644
--- a/go/loader/loader_test.go
+++ b/go/loader/loader_test.go
@@ -13,6 +13,8 @@
import (
"fmt"
"go/build"
+ "go/constant"
+ "go/types"
"path/filepath"
"reflect"
"sort"
@@ -477,6 +479,60 @@
}
}
+func TestVendorCwdIssue16580(t *testing.T) {
+ // Regression test for Go issue 16580.
+ // Import decls in "created" packages were vendor-resolved
+ // w.r.t. cwd, not the parent directory of the package's files.
+ ctxt := fakeContext(map[string]string{
+ "a": ``, // mkdir a
+ "a/vendor": ``, // mkdir a/vendor
+ "a/vendor/b": `package b; const X = true`,
+ "b": `package b; const X = false`,
+ })
+ for _, test := range []struct {
+ filename, cwd string
+ want bool // expected value of b.X; depends on filename, not on cwd
+ }{
+ {filename: "c.go", cwd: "/go/src", want: false},
+ {filename: "c.go", cwd: "/go/src/a", want: false},
+ {filename: "c.go", cwd: "/go/src/a/b", want: false},
+ {filename: "c.go", cwd: "/go/src/a/vendor/b", want: false},
+
+ {filename: "/go/src/a/c.go", cwd: "/go/src", want: true},
+ {filename: "/go/src/a/c.go", cwd: "/go/src/a", want: true},
+ {filename: "/go/src/a/c.go", cwd: "/go/src/a/b", want: true},
+ {filename: "/go/src/a/c.go", cwd: "/go/src/a/vendor/b", want: true},
+
+ {filename: "/go/src/c/c.go", cwd: "/go/src", want: false},
+ {filename: "/go/src/c/c.go", cwd: "/go/src/a", want: false},
+ {filename: "/go/src/c/c.go", cwd: "/go/src/a/b", want: false},
+ {filename: "/go/src/c/c.go", cwd: "/go/src/a/vendor/b", want: false},
+ } {
+ conf := loader.Config{
+ Cwd: test.cwd,
+ Build: ctxt,
+ }
+ f, err := conf.ParseFile(test.filename, `package dummy; import "b"; const X = b.X`)
+ if err != nil {
+ t.Fatal(f)
+ }
+ conf.CreateFromFiles("dummy", f)
+
+ prog, err := conf.Load()
+ if err != nil {
+ t.Errorf("%+v: Load failed: %v", test, err)
+ continue
+ }
+
+ x := constant.BoolVal(prog.Created[0].Pkg.Scope().Lookup("X").(*types.Const).Val())
+ if x != test.want {
+ t.Errorf("%+v: b.X = %t", test, x)
+ }
+ }
+
+ // TODO(adonovan): also test imports within XTestGoFiles.
+}
+
// TODO(adonovan): more Load tests:
//
// failures: