Skip to content

Commit d6c2f1e

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modload: use a structured error for 'ambiguous import'
This consolidates the construction of 'ambiguous import' errors to a single location, ensuring consistency, and lays the groundwork for automatic resolution in the future. While we're at it, change "found" to "found package" to try to make the cause of the error clearer. Updates #32128 Updates #27899 Change-Id: I14a93593320e5c60d20b0eb686d0d5355763c30c Reviewed-on: https://go-review.googlesource.com/c/go/+/196298 Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 83feeed commit d6c2f1e

File tree

2 files changed

+86
-12
lines changed

2 files changed

+86
-12
lines changed

src/cmd/go/internal/modload/import.go

+37-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package modload
66

77
import (
8-
"bytes"
98
"errors"
109
"fmt"
1110
"go/build"
@@ -52,6 +51,41 @@ func (e *ImportMissingError) Unwrap() error {
5251
return e.QueryErr
5352
}
5453

54+
// An AmbiguousImportError indicates an import of a package found in multiple
55+
// modules in the build list, or found in both the main module and its vendor
56+
// directory.
57+
type AmbiguousImportError struct {
58+
ImportPath string
59+
Dirs []string
60+
Modules []module.Version // Either empty or 1:1 with Dirs.
61+
}
62+
63+
func (e *AmbiguousImportError) Error() string {
64+
locType := "modules"
65+
if len(e.Modules) == 0 {
66+
locType = "directories"
67+
}
68+
69+
var buf strings.Builder
70+
fmt.Fprintf(&buf, "ambiguous import: found package %s in multiple %s:", e.ImportPath, locType)
71+
72+
for i, dir := range e.Dirs {
73+
buf.WriteString("\n\t")
74+
if i < len(e.Modules) {
75+
m := e.Modules[i]
76+
buf.WriteString(m.Path)
77+
if m.Version != "" {
78+
fmt.Fprintf(&buf, " %s", m.Version)
79+
}
80+
fmt.Fprintf(&buf, " (%s)", dir)
81+
} else {
82+
buf.WriteString(dir)
83+
}
84+
}
85+
86+
return buf.String()
87+
}
88+
5589
// Import finds the module and directory in the build list
5690
// containing the package with the given import path.
5791
// The answer must be unique: Import returns an error
@@ -96,7 +130,7 @@ func Import(path string) (m module.Version, dir string, err error) {
96130
mainDir, mainOK := dirInModule(path, targetPrefix, ModRoot(), true)
97131
vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
98132
if mainOK && vendorOK {
99-
return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir)
133+
return module.Version{}, "", &AmbiguousImportError{ImportPath: path, Dirs: []string{mainDir, vendorDir}}
100134
}
101135
// Prefer to return main directory if there is one,
102136
// Note that we're not checking that the package exists.
@@ -136,16 +170,7 @@ func Import(path string) (m module.Version, dir string, err error) {
136170
return mods[0], dirs[0], nil
137171
}
138172
if len(mods) > 0 {
139-
var buf bytes.Buffer
140-
fmt.Fprintf(&buf, "ambiguous import: found %s in multiple modules:", path)
141-
for i, m := range mods {
142-
fmt.Fprintf(&buf, "\n\t%s", m.Path)
143-
if m.Version != "" {
144-
fmt.Fprintf(&buf, " %s", m.Version)
145-
}
146-
fmt.Fprintf(&buf, " (%s)", dirs[i])
147-
}
148-
return module.Version{}, "", errors.New(buf.String())
173+
return module.Version{}, "", &AmbiguousImportError{ImportPath: path, Dirs: dirs, Modules: mods}
149174
}
150175

151176
// Look up module containing the package, for addition to the build list.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
env GO111MODULE=on
2+
3+
cd $WORK
4+
5+
# An import provided by two different modules should be flagged as an error.
6+
! go build ./importx
7+
stderr '^importx[/\\]importx.go:2:8: ambiguous import: found package example.com/a/x in multiple modules:\n\texample.com/a v0.1.0 \('$WORK'[/\\]a[/\\]x\)\n\texample.com/a/x v0.1.0 \('$WORK'[/\\]ax\)$'
8+
9+
# However, it should not be an error if that import is unused.
10+
go build ./importy
11+
12+
# An import provided by both the main module and the vendor directory
13+
# should be flagged as an error only when -mod=vendor is set.
14+
# TODO: This error message is a bit redundant.
15+
mkdir vendor/example.com/m/importy
16+
cp $WORK/importy/importy.go vendor/example.com/m/importy/importy.go
17+
go build example.com/m/importy
18+
! go build -mod=vendor example.com/m/importy
19+
stderr '^can.t load package: package example.com/m/importy: ambiguous import: found package example.com/m/importy in multiple directories:\n\t'$WORK'[/\\]importy\n\t'$WORK'[/\\]vendor[/\\]example.com[/\\]m[/\\]importy$'
20+
21+
-- $WORK/go.mod --
22+
module example.com/m
23+
go 1.14
24+
require (
25+
example.com/a v0.1.0
26+
example.com/a/x v0.1.0
27+
)
28+
replace (
29+
example.com/a v0.1.0 => ./a
30+
example.com/a/x v0.1.0 => ./ax
31+
)
32+
-- $WORK/importx/importx.go --
33+
package importx
34+
import _ "example.com/a/x"
35+
-- $WORK/importy/importy.go --
36+
package importy
37+
import _ "example.com/a/y"
38+
-- $WORK/a/go.mod --
39+
module example.com/a
40+
go 1.14
41+
-- $WORK/a/x/x.go --
42+
package x
43+
-- $WORK/a/y/y.go --
44+
package y
45+
-- $WORK/ax/go.mod --
46+
module example.com/a/x
47+
go 1.14
48+
-- $WORK/ax/x.go --
49+
package x

0 commit comments

Comments
 (0)