Skip to content

Commit b9edee3

Browse files
author
Bryan C. Mills
committed
cmd/go: check for source files in relative paths before attempting to determine the package path
This is a more minimial fix for the immediate symptom of 32917 and 30590, but does not improve 'list -e' behavior or error messages resulting from other package loading issues. Fixes #32917 Fixes #30590 Change-Id: I6088d14d864410159ebf228d9392d186322fd2a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/185417 Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 4b36588 commit b9edee3

File tree

5 files changed

+96
-17
lines changed

5 files changed

+96
-17
lines changed

src/cmd/go/internal/modload/load.go

+54-12
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,31 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
100100
dir = filepath.Clean(dir)
101101
}
102102

103+
// golang.org/issue/32917: We should resolve a relative path to a
104+
// package path only if the relative path actually contains the code
105+
// for that package.
106+
if !dirContainsPackage(dir) {
107+
// If we're outside of a module, ensure that the failure mode
108+
// indicates that.
109+
ModRoot()
110+
111+
// If the directory is local but does not exist, don't return it
112+
// while loader is iterating, since this might trigger a fetch.
113+
// After loader is done iterating, we still need to return the
114+
// path, so that "go list -e" produces valid output.
115+
if !iterating {
116+
// We don't have a valid path to resolve to, so report the
117+
// unresolved path.
118+
m.Pkgs = append(m.Pkgs, pkg)
119+
}
120+
continue
121+
}
122+
103123
// Note: The checks for @ here are just to avoid misinterpreting
104124
// the module cache directories (formerly GOPATH/src/mod/[email protected]/bar).
105125
// It's not strictly necessary but helpful to keep the checks.
106126
if modRoot != "" && dir == modRoot {
107-
pkg = Target.Path
127+
pkg = targetPrefix
108128
} else if modRoot != "" && strings.HasPrefix(dir, modRoot+string(filepath.Separator)) && !strings.Contains(dir[len(modRoot):], "@") {
109129
suffix := filepath.ToSlash(dir[len(modRoot):])
110130
if strings.HasPrefix(suffix, "/vendor/") {
@@ -121,7 +141,13 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
121141
continue
122142
}
123143
} else {
124-
pkg = Target.Path + suffix
144+
modPkg := targetPrefix + suffix
145+
if _, ok := dirInModule(modPkg, targetPrefix, modRoot, true); ok {
146+
pkg = modPkg
147+
} else if !iterating {
148+
ModRoot()
149+
base.Errorf("go: directory %s is outside main module", base.ShortPath(dir))
150+
}
125151
}
126152
} else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
127153
pkg = filepath.ToSlash(sub)
@@ -134,16 +160,6 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
134160
base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
135161
}
136162
}
137-
info, err := os.Stat(dir)
138-
if err != nil || !info.IsDir() {
139-
// If the directory is local but does not exist, don't return it
140-
// while loader is iterating, since this would trigger a fetch.
141-
// After loader is done iterating, we still need to return the
142-
// path, so that "go list -e" produces valid output.
143-
if iterating {
144-
continue
145-
}
146-
}
147163
m.Pkgs = append(m.Pkgs, pkg)
148164
}
149165

@@ -247,6 +263,32 @@ func pathInModuleCache(dir string) string {
247263
return ""
248264
}
249265

266+
var dirContainsPackageCache sync.Map // absolute dir → bool
267+
268+
func dirContainsPackage(dir string) bool {
269+
isPkg, ok := dirContainsPackageCache.Load(dir)
270+
if !ok {
271+
_, err := cfg.BuildContext.ImportDir(dir, 0)
272+
if err == nil {
273+
isPkg = true
274+
} else {
275+
if fi, statErr := os.Stat(dir); statErr != nil || !fi.IsDir() {
276+
// A non-directory or inaccessible directory is not a Go package.
277+
isPkg = false
278+
} else if _, noGo := err.(*build.NoGoError); noGo {
279+
// A directory containing no Go source files is not a Go package.
280+
isPkg = false
281+
} else {
282+
// An error other than *build.NoGoError indicates that the package exists
283+
// but has some other problem (such as a syntax error).
284+
isPkg = true
285+
}
286+
}
287+
isPkg, _ = dirContainsPackageCache.LoadOrStore(dir, isPkg)
288+
}
289+
return isPkg.(bool)
290+
}
291+
250292
// ImportFromFiles adds modules to the build list as needed
251293
// to satisfy the imports in the named Go source files.
252294
func ImportFromFiles(gofiles []string) {
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
env GO111MODULE=on
2+
3+
# golang.org/issue/32917 and golang.org/issue/28459: 'go build' and 'go test'
4+
# in an empty directory should refer to the path '.' and should not attempt
5+
# to resolve an external module.
6+
cd dir
7+
! go get .
8+
stderr 'go get \.: path .* is not a package in module rooted at .*[/\\]dir$'
9+
! go list
10+
! stderr 'cannot find module providing package'
11+
stderr '^can.t load package: package \.: no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
12+
13+
cd subdir
14+
! go list
15+
! stderr 'cannot find module providing package'
16+
stderr '^can.t load package: package \.: no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir$'
17+
cd ..
18+
19+
# golang.org/issue/30590: if a package is found in the filesystem
20+
# but is not in the main module, the error message should not say
21+
# "cannot find module providing package", and we shouldn't try
22+
# to find a module providing the package.
23+
! go list ./othermodule
24+
! stderr 'cannot find module providing package'
25+
stderr 'go: directory othermodule is outside main module'
26+
27+
-- dir/go.mod --
28+
module example.com
29+
go 1.13
30+
-- dir/subdir/README --
31+
There are no Go source files in this directory.
32+
-- dir/othermodule/go.mod --
33+
module example.com/othermodule
34+
go 1.13
35+
-- dir/othermodule/om.go --
36+
package othermodule

src/cmd/go/testdata/script/mod_fs_patterns.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ stderr 'import lookup disabled'
3434

3535
! go build -mod=readonly ./nonexist
3636
! stderr 'import lookup disabled'
37-
stderr 'unknown import path "m/nonexist": cannot find package'
37+
stderr '^can.t load package: package ./nonexist: cannot find package "." in:\n\t'$WORK'[/\\]gopath[/\\]src[/\\]x[/\\]nonexist$'
3838

3939
! go build -mod=readonly ./go.mod
4040
! stderr 'import lookup disabled'
41-
stderr 'unknown import path "m/go.mod": cannot find package'
41+
stderr 'can.t load package: package ./go.mod: cannot find package'
4242

4343
-- x/go.mod --
4444
module m

src/cmd/go/testdata/script/mod_list_dir.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ stdout ^math$
1212
go list -f '{{.ImportPath}}' .
1313
stdout ^x$
1414
! go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/[email protected]
15-
stderr 'unknown import path "rsc.io/quote": cannot find package'
15+
stderr '^can.t load package: package '$WORK'[/\\]gopath/pkg/mod/rsc.io/[email protected]: can only use path@version syntax with .go get.'
16+
1617
go list -e -f '{{with .Error}}{{.}}{{end}}' $GOPATH/pkg/mod/rsc.io/[email protected]
17-
stdout 'unknown import path "rsc.io/quote": cannot find package'
18+
stdout '^package '$WORK'[/\\]gopath/pkg/mod/rsc.io/quote@v1.5.2: can only use path@version syntax with .go get.'
1819
go mod download rsc.io/[email protected]
1920
go list -f '{{.ImportPath}}' $GOPATH/pkg/mod/rsc.io/[email protected]
2021
stdout '^rsc.io/quote$'

src/cmd/go/testdata/script/mod_list_replace_dir.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ env GO111MODULE=on
66
go mod download
77

88
! go list $GOPATH/pkg/mod/rsc.io/[email protected]
9-
stderr 'outside available modules'
9+
stderr 'can only use path@version syntax with .go get.'
1010

1111
go list $GOPATH/pkg/mod/rsc.io/[email protected]
1212
stdout 'rsc.io/quote'

0 commit comments

Comments
 (0)