cue/errors: remove List from public API

and use Errors, Sanitize and Promote to replace
the missing functionality.

Removing List greatly simplifies the API and also avoid
the nil-interface issue, making it easier to keep code
correct.

Change-Id: Idd3feedc6efe7ad17c104ee1bedea615ee15383d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2501
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index d203675..d64a5af 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -45,7 +45,7 @@
 	// inserting. For now, we accept errors that did not make it up to the tree.
 	result := v.walk(f)
 	if isBottom(result) {
-		if v.errors.Len() > 0 {
+		if v.errors != nil {
 			return v.errors
 		}
 		v := newValueRoot(v.ctx(), result)
@@ -87,7 +87,7 @@
 	// make unique per level to avoid reuse of structs being an issue.
 	astMap map[ast.Node]scope
 
-	errors errors.List
+	errors errors.Error
 }
 
 func (s *astState) mapScope(n ast.Node) (m scope) {
@@ -126,7 +126,7 @@
 }
 
 func (v *astVisitor) errf(n ast.Node, format string, args ...interface{}) evaluated {
-	v.astState.errors.Add(&nodeError{
+	v.astState.errors = errors.Append(v.astState.errors, &nodeError{
 		path:    v.appendPath(nil),
 		n:       n,
 		Message: errors.NewMessage(format, args),
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 95b96ca..96ea535 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -250,10 +250,10 @@
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
-			ctx, root, errs := compileFileWithErrors(t, tc.in)
+			ctx, root, err := compileFileWithErrors(t, tc.in)
 			buf := &bytes.Buffer{}
-			if len(errs) > 0 {
-				errors.Print(buf, errs, nil)
+			if err != nil {
+				errors.Print(buf, err, nil)
 			}
 			buf.WriteString(debugStr(ctx, root))
 			got := buf.String()
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index 7b9277b..f5c5619 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -148,7 +148,7 @@
 	}
 }
 
-// Promote converts a regular Go error to an Error if it isn't already so.
+// Promote converts a regular Go error to an Error if it isn't already one.
 func Promote(err error, msg string) Error {
 	switch x := err.(type) {
 	case Error:
@@ -195,7 +195,7 @@
 	switch x := a.(type) {
 	case nil:
 		return b
-	case List:
+	case list:
 		return appendToList(x, b)
 	}
 	// Preserve order of errors.
@@ -212,7 +212,7 @@
 	switch x := err.(type) {
 	case nil:
 		return nil
-	case List:
+	case list:
 		return []Error(x)
 	case Error:
 		return []Error{x}
@@ -221,43 +221,43 @@
 	}
 }
 
-func appendToList(list List, err Error) List {
+func appendToList(a list, err Error) list {
 	switch x := err.(type) {
 	case nil:
-		return list
-	case List:
-		if list == nil {
+		return a
+	case list:
+		if a == nil {
 			return x
 		}
-		return append(list, x...)
+		return append(a, x...)
 	default:
-		return append(list, err)
+		return append(a, err)
 	}
 }
 
-// List is a list of Errors.
-// The zero value for an List is an empty List ready to use.
-type List []Error
+// list is a list of Errors.
+// The zero value for an list is an empty list ready to use.
+type list []Error
 
 // AddNewf adds an Error with given position and error message to an List.
-func (p *List) AddNewf(pos token.Pos, msg string, args ...interface{}) {
+func (p *list) AddNewf(pos token.Pos, msg string, args ...interface{}) {
 	err := &posError{pos: pos, Message: Message{format: msg, args: args}}
 	*p = append(*p, err)
 }
 
 // Add adds an Error with given position and error message to an List.
-func (p *List) Add(err Error) {
+func (p *list) Add(err Error) {
 	*p = appendToList(*p, err)
 }
 
 // Reset resets an List to no errors.
-func (p *List) Reset() { *p = (*p)[0:0] }
+func (p *list) Reset() { *p = (*p)[0:0] }
 
 // List implements the sort Interface.
-func (p List) Len() int      { return len(p) }
-func (p List) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p list) Len() int      { return len(p) }
+func (p list) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
 
-func (p List) Less(i, j int) bool {
+func (p list) Less(i, j int) bool {
 	if c := comparePos(p[i].Position(), p[j].Position()); c != 0 {
 		return c == -1
 	}
@@ -315,16 +315,29 @@
 	return true
 }
 
+// Sanitize sorts multiple errors and removes duplicates on a best effort basis.
+// If err represents a single or no error, it returns the error as is.
+func Sanitize(err Error) Error {
+	if l, ok := err.(list); ok && err != nil {
+		a := make(list, len(l))
+		copy(a, l)
+		a.Sort()
+		a.RemoveMultiples()
+		return a
+	}
+	return err
+}
+
 // Sort sorts an List. *posError entries are sorted by position,
 // other errors are sorted by error message, and before any *posError
 // entry.
 //
-func (p List) Sort() {
+func (p list) Sort() {
 	sort.Sort(p)
 }
 
 // RemoveMultiples sorts an List and removes all but the first error per line.
-func (p *List) RemoveMultiples() {
+func (p *list) RemoveMultiples() {
 	p.Sort()
 	var last Error
 	i := 0
@@ -343,13 +356,13 @@
 }
 
 // An List implements the error interface.
-func (p List) Error() string {
+func (p list) Error() string {
 	format, args := p.Msg()
 	return fmt.Sprintf(format, args...)
 }
 
 // Msg reports the unformatted error message for the first error, if any.
-func (p List) Msg() (format string, args []interface{}) {
+func (p list) Msg() (format string, args []interface{}) {
 	switch len(p) {
 	case 0:
 		return "no errors", nil
@@ -361,7 +374,7 @@
 }
 
 // Position reports the primary position for the first error, if any.
-func (p List) Position() token.Pos {
+func (p list) Position() token.Pos {
 	if len(p) == 0 {
 		return token.NoPos
 	}
@@ -369,7 +382,7 @@
 }
 
 // InputPositions reports the input positions for the first error, if any.
-func (p List) InputPositions() []token.Pos {
+func (p list) InputPositions() []token.Pos {
 	if len(p) == 0 {
 		return nil
 	}
@@ -377,7 +390,7 @@
 }
 
 // Path reports the path location of the first error, if any.
-func (p List) Path() []string {
+func (p list) Path() []string {
 	if len(p) == 0 {
 		return nil
 	}
@@ -386,7 +399,7 @@
 
 // Err returns an error equivalent to this error list.
 // If the list is empty, Err returns nil.
-func (p List) Err() error {
+func (p list) Err() error {
 	if len(p) == 0 {
 		return nil
 	}
diff --git a/cue/errors/errors_test.go b/cue/errors/errors_test.go
index 76c7e68..f6bdc35 100644
--- a/cue/errors/errors_test.go
+++ b/cue/errors/errors_test.go
@@ -43,7 +43,7 @@
 	}
 	tests := []struct {
 		name string
-		p    *List
+		p    *list
 		args args
 	}{
 		// TODO: Add test cases.
@@ -56,7 +56,7 @@
 func TestErrorList_Reset(t *testing.T) {
 	tests := []struct {
 		name string
-		p    *List
+		p    *list
 	}{
 		// TODO: Add test cases.
 	}
@@ -68,14 +68,14 @@
 func TestErrorList_Len(t *testing.T) {
 	tests := []struct {
 		name string
-		p    List
+		p    list
 		want int
 	}{
 		// TODO: Add test cases.
 	}
 	for _, tt := range tests {
 		if got := tt.p.Len(); got != tt.want {
-			t.Errorf("%q. List.Len() = %v, want %v", tt.name, got, tt.want)
+			t.Errorf("%q. list.Len() = %v, want %v", tt.name, got, tt.want)
 		}
 	}
 }
@@ -87,7 +87,7 @@
 	}
 	tests := []struct {
 		name string
-		p    List
+		p    list
 		args args
 	}{
 		// TODO: Add test cases.
@@ -104,7 +104,7 @@
 	}
 	tests := []struct {
 		name string
-		p    List
+		p    list
 		args args
 		want bool
 	}{
@@ -112,7 +112,7 @@
 	}
 	for _, tt := range tests {
 		if got := tt.p.Less(tt.args.i, tt.args.j); got != tt.want {
-			t.Errorf("%q. List.Less() = %v, want %v", tt.name, got, tt.want)
+			t.Errorf("%q. list.Less() = %v, want %v", tt.name, got, tt.want)
 		}
 	}
 }
@@ -120,7 +120,7 @@
 func TestErrorList_Sort(t *testing.T) {
 	tests := []struct {
 		name string
-		p    List
+		p    list
 	}{
 		// TODO: Add test cases.
 	}
@@ -132,7 +132,7 @@
 func TestErrorList_RemoveMultiples(t *testing.T) {
 	tests := []struct {
 		name string
-		p    *List
+		p    *list
 	}{
 		// TODO: Add test cases.
 	}
@@ -144,14 +144,14 @@
 func TestErrorList_Error(t *testing.T) {
 	tests := []struct {
 		name string
-		p    List
+		p    list
 		want string
 	}{
 		// TODO: Add test cases.
 	}
 	for _, tt := range tests {
 		if got := tt.p.Error(); got != tt.want {
-			t.Errorf("%q. List.Error() = %v, want %v", tt.name, got, tt.want)
+			t.Errorf("%q. list.Error() = %v, want %v", tt.name, got, tt.want)
 		}
 	}
 }
@@ -159,14 +159,14 @@
 func TestErrorList_Err(t *testing.T) {
 	tests := []struct {
 		name    string
-		p       List
+		p       list
 		wantErr bool
 	}{
 		// TODO: Add test cases.
 	}
 	for _, tt := range tests {
 		if err := tt.p.Err(); (err != nil) != tt.wantErr {
-			t.Errorf("%q. List.Err() error = %v, wantErr %v", tt.name, err, tt.wantErr)
+			t.Errorf("%q. list.Err() error = %v, wantErr %v", tt.name, err, tt.wantErr)
 		}
 	}
 }
diff --git a/cue/load/import.go b/cue/load/import.go
index b011fcb..e0618cb 100644
--- a/cue/load/import.go
+++ b/cue/load/import.go
@@ -147,7 +147,7 @@
 
 	rewriteFiles(p, root, false)
 	if errs := fp.finalize(); errs != nil {
-		for _, e := range errs {
+		for _, e := range errors.Errors(errs) {
 			p.ReportError(e)
 		}
 		return p
@@ -293,7 +293,7 @@
 	c   *Config
 	pkg *build.Instance
 
-	err errors.List
+	err errors.Error
 }
 
 func newFileProcessor(c *Config, p *build.Instance) *fileProcessor {
@@ -305,13 +305,13 @@
 	}
 }
 
-func (fp *fileProcessor) finalize() errors.List {
+func (fp *fileProcessor) finalize() errors.Error {
 	p := fp.pkg
 	if fp.err != nil {
 		return fp.err
 	}
 	if len(p.CUEFiles) == 0 && !fp.c.DataFiles {
-		fp.err.Add(&noCUEError{Package: p, Dir: p.Dir, Ignored: len(p.IgnoredCUEFiles) > 0})
+		fp.err = errors.Append(fp.err, &noCUEError{Package: p, Dir: p.Dir, Ignored: len(p.IgnoredCUEFiles) > 0})
 		return fp.err
 	}
 
@@ -337,7 +337,7 @@
 	p := fp.pkg
 
 	badFile := func(err errors.Error) bool {
-		fp.err.Add(err)
+		fp.err = errors.Append(fp.err, err)
 		p.InvalidCUEFiles = append(p.InvalidCUEFiles, fullPath)
 		return true
 	}
@@ -357,7 +357,6 @@
 
 	pf, perr := parser.ParseFile(filename, data, parser.ImportsOnly, parser.ParseComments)
 	if perr != nil {
-		// should always be an errors.List, but just in case.
 		badFile(errors.Promote(perr, "add failed"))
 		return true
 	}
diff --git a/cue/load/loader.go b/cue/load/loader.go
index aa8e38b..7dd8e90 100644
--- a/cue/load/loader.go
+++ b/cue/load/loader.go
@@ -133,7 +133,7 @@
 	}
 	pkg.Dir = cfg.Dir
 	rewriteFiles(pkg, pkg.Dir, true)
-	for _, err := range fp.finalize() { // ImportDir(&ctxt, dir, 0)
+	for _, err := range errors.Errors(fp.finalize()) { // ImportDir(&ctxt, dir, 0)
 		pkg.ReportError(err)
 	}
 	// TODO: Support module importing.
diff --git a/cue/parser/error_test.go b/cue/parser/error_test.go
index 0118acb..e434d05 100644
--- a/cue/parser/error_test.go
+++ b/cue/parser/error_test.go
@@ -108,7 +108,7 @@
 // compareErrors compares the map of expected error messages with the list
 // of found errors and reports discrepancies.
 //
-func compareErrors(t *testing.T, file *token.File, expected map[token.Pos]string, found errors.List) {
+func compareErrors(t *testing.T, file *token.File, expected map[token.Pos]string, found []errors.Error) {
 	t.Helper()
 	for _, error := range found {
 		// error.Pos is a Position, but we want
@@ -157,12 +157,7 @@
 
 	f, err := ParseFile(filename, src, DeclarationErrors, AllErrors)
 	file := f.Pos().File()
-	found, ok := err.(errors.List)
-	if err != nil && !ok {
-		t.Error(err)
-		return
-	}
-	found.RemoveMultiples()
+	found := errors.Errors(err)
 
 	// we are expecting the following errors
 	// (collect these after parsing a file so that it is found in the file set)
diff --git a/cue/parser/interface.go b/cue/parser/interface.go
index 980cb2e..0711b20 100644
--- a/cue/parser/interface.go
+++ b/cue/parser/interface.go
@@ -18,6 +18,7 @@
 
 import (
 	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal/source"
 )
@@ -127,8 +128,7 @@
 			}
 		}
 
-		pp.errors.Sort()
-		err = pp.errors.Err()
+		err = errors.Sanitize(pp.errors)
 	}()
 
 	// parse source
@@ -159,8 +159,7 @@
 		if p.panicking {
 			_ = recover()
 		}
-		p.errors.Sort()
-		err = p.errors.Err()
+		err = errors.Sanitize(p.errors)
 	}()
 
 	// parse expr
@@ -180,9 +179,8 @@
 		p.expect(token.EOF)
 	}
 
-	if p.errors.Len() > 0 {
-		p.errors.Sort()
-		return nil, p.errors.Err()
+	if p.errors != nil {
+		return nil, p.errors
 	}
 	resolveExpr(e, p.errf)
 
diff --git a/cue/parser/parser.go b/cue/parser/parser.go
index b84f6b7..404d830 100644
--- a/cue/parser/parser.go
+++ b/cue/parser/parser.go
@@ -29,7 +29,7 @@
 // The parser structure holds the parser's internal state.
 type parser struct {
 	file    *token.File
-	errors  errors.List
+	errors  errors.Error
 	scanner scanner.Scanner
 
 	// Tracing/debugging
@@ -71,7 +71,7 @@
 		m = scanner.ScanComments
 	}
 	eh := func(pos token.Pos, msg string, args []interface{}) {
-		p.errors.AddNewf(pos, msg, args...)
+		p.errors = errors.Append(p.errors, errors.Newf(pos, msg, args...))
 	}
 	p.scanner.Init(p.file, src, eh, m)
 
@@ -345,8 +345,9 @@
 	// as the last recorded error and stop parsing if there are more than
 	// 10 errors.
 	if p.mode&allErrorsMode == 0 {
-		n := len(p.errors)
-		if n > 0 && p.errors[n-1].Position().Line() == ePos.Line() {
+		errors := errors.Errors(p.errors)
+		n := len(errors)
+		if n > 0 && errors[n-1].Position().Line() == ePos.Line() {
 			return // discard - likely a spurious error
 		}
 		if n > 10 {
@@ -355,7 +356,7 @@
 		}
 	}
 
-	p.errors.AddNewf(ePos, msg, args...)
+	p.errors = errors.Append(p.errors, errors.Newf(ePos, msg, args...))
 }
 
 func (p *parser) errorExpected(pos token.Pos, obj string) {
@@ -1314,7 +1315,7 @@
 
 	// Don't bother parsing the rest if we had errors scanning the first
 	// Likely not a Go source file at all.
-	if p.errors.Len() != 0 {
+	if p.errors != nil {
 		return nil
 	}
 
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 94e2ab4..193a6ef 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -17,16 +17,14 @@
 import (
 	"flag"
 	"testing"
-
-	"cuelang.org/go/cue/errors"
 )
 
 var traceOn = flag.Bool("debug", false, "enable tracing")
 
-func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, errors.List) {
+func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, error) {
 	t.Helper()
-	ctx, inst, errs := compileInstance(t, body)
-	return ctx, inst.rootValue.evalPartial(ctx).(*structLit), errs
+	ctx, inst, err := compileInstance(t, body)
+	return ctx, inst.rootValue.evalPartial(ctx).(*structLit), err
 }
 
 func compileFile(t *testing.T, body string) (*context, *structLit) {
@@ -38,27 +36,19 @@
 	return ctx, inst.rootValue.evalPartial(ctx).(*structLit)
 }
 
-func compileInstance(t *testing.T, body string) (*context, *Instance, errors.List) {
+func compileInstance(t *testing.T, body string) (*context, *Instance, error) {
 	t.Helper()
 
 	var r Runtime
-	x, err := r.Parse("test", body)
-	ctx := r.index().newContext()
+	inst, err := r.Parse("test", body)
 
-	switch errs := err.(type) {
-	case nil:
-		var r Runtime
-		inst, _ := r.Parse("test", body)
-		return r.index().newContext(), inst, nil
-	case errors.List:
+	if err != nil {
 		x := newIndex().NewInstance(nil)
 		ctx := x.newContext()
-
-		return ctx, x, errs
-	default:
-		t.Fatal(err)
+		return ctx, x, err
 	}
-	return ctx, x, nil
+
+	return r.index().newContext(), inst, nil
 }
 
 func rewriteHelper(t *testing.T, cases []testCase, r rewriteMode) {
diff --git a/cue/scanner/scanner_test.go b/cue/scanner/scanner_test.go
index a1b8741..6748ce2 100644
--- a/cue/scanner/scanner_test.go
+++ b/cue/scanner/scanner_test.go
@@ -651,9 +651,9 @@
 		"//line File1:1\n" +
 		"~ ~ ~" // original file, line 1 again
 
-	var list errors.List
+	var list errors.Error
 	eh := func(pos token.Pos, msg string, args []interface{}) {
-		list.AddNewf(pos, msg, args...)
+		list = errors.Append(list, errors.Newf(pos, msg, args...))
 	}
 
 	var s Scanner
@@ -664,24 +664,19 @@
 		}
 	}
 
-	if len(list) != s.ErrorCount {
-		t.Errorf("found %d errors, expected %d", len(list), s.ErrorCount)
+	n := len(errors.Errors(list))
+	if n != s.ErrorCount {
+		t.Errorf("found %d errors, expected %d", n, s.ErrorCount)
 	}
 
-	if len(list) != 9 {
-		t.Errorf("found %d raw errors, expected 9", len(list))
+	if n != 9 {
+		t.Errorf("found %d raw errors, expected 9", n)
 		errors.Print(os.Stderr, list, nil)
 	}
 
-	list.Sort()
-	if len(list) != 9 {
-		t.Errorf("found %d sorted errors, expected 9", len(list))
-		errors.Print(os.Stderr, list, nil)
-	}
-
-	list.RemoveMultiples()
-	if len(list) != 4 {
-		t.Errorf("found %d one-per-line errors, expected 4", len(list))
+	n = len(errors.Errors(errors.Sanitize(list)))
+	if n != 4 {
+		t.Errorf("found %d one-per-line errors, expected 4", n)
 		errors.Print(os.Stderr, list, nil)
 	}
 }
diff --git a/cue/types.go b/cue/types.go
index ee68d3b..38e6a70 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1339,35 +1339,30 @@
 	}
 }
 
-// Validate reports any errors, recursively. The returned error may be an
-// errors.List reporting multiple errors, where the total number of errors
-// reported may be less than the actual number.
+// Validate reports any errors, recursively. The returned error may represent
+// more than one error, retrievable with errors.Errors, if more than one
+// exists.
 func (v Value) Validate(opts ...Option) error {
 	o := getOptions(opts)
-	list := errors.List{}
+	var errs errors.Error
 	v.Walk(func(v Value) bool {
 		if err := v.checkKind(v.ctx(), bottomKind); err != nil {
 			if !o.concrete && isIncomplete(v.eval(v.ctx())) {
 				return false
 			}
-			list.Add(v.toErr(err))
-			if len(list) > 50 {
+			errs = errors.Append(errs, v.toErr(err))
+			if len(errors.Errors(errs)) > 50 {
 				return false // mostly to avoid some hypothetical cycle issue
 			}
 		}
 		if o.concrete {
 			if err := isGroundRecursive(v.ctx(), v.eval(v.ctx())); err != nil {
-				list.Add(v.toErr(err))
+				errs = errors.Append(errs, v.toErr(err))
 			}
 		}
 		return true
 	}, nil)
-	if len(list) > 0 {
-		list.Sort()
-		list.RemoveMultiples() // TODO: use RemoveMultiples when it is fixed
-		return list
-	}
-	return nil
+	return errors.Sanitize(errs)
 }
 
 func isGroundRecursive(ctx *context, v value) *bottom {
diff --git a/encoding/protobuf/protobuf.go b/encoding/protobuf/protobuf.go
index 8d180d9..d813438 100644
--- a/encoding/protobuf/protobuf.go
+++ b/encoding/protobuf/protobuf.go
@@ -81,7 +81,7 @@
 	instCache map[string]*build.Instance
 	imports   map[string]*build.Instance
 
-	errs errors.List
+	errs errors.Error
 	done bool
 }
 
@@ -114,11 +114,11 @@
 // Err returns the errors accumulated during testing. The returned error may be
 // of type cuelang.org/go/cue/errors.List.
 func (b *Extractor) Err() error {
-	return b.errs.Err()
+	return b.errs
 }
 
 func (b *Extractor) addErr(err error) {
-	b.errs.Add(errors.Promote(err, "unknown error"))
+	b.errs = errors.Append(b.errs, errors.Promote(err, "unknown error"))
 }
 
 // AddFile adds a proto definition file to be converted into CUE by the builder.
@@ -131,8 +131,10 @@
 //
 func (b *Extractor) AddFile(filename string, src interface{}) error {
 	if b.done {
-		b.errs.Add(errors.Newf(token.NoPos, "protobuf: cannot call AddFile: Instances was already called"))
-		return b.Err()
+		err := errors.Newf(token.NoPos,
+			"protobuf: cannot call AddFile: Instances was already called")
+		b.errs = errors.Append(b.errs, err)
+		return err
 	}
 	if b.root != b.cwd && !filepath.IsAbs(filename) {
 		filename = filepath.Join(b.root, filename)
@@ -235,7 +237,7 @@
 		return instances[i].ImportPath < instances[j].ImportPath
 	})
 
-	if err := b.errs.Err(); err != nil {
+	if err != nil {
 		return instances, err
 	}
 	return instances, nil
@@ -247,8 +249,10 @@
 	}
 	importPath := p.goPkgPath
 	if importPath == "" {
-		b.errs.AddNewf(token.NoPos, "no go_package for proto package %q in file %s", p.id, p.file.Filename)
-		// TODO: fine an alternative. Is proto package good enough?
+		err := errors.Newf(token.NoPos,
+			"no go_package for proto package %q in file %s", p.id, p.file.Filename)
+		b.errs = errors.Append(b.errs, err)
+		// TODO: find an alternative. Is proto package good enough?
 		return nil
 	}
 
@@ -263,10 +267,11 @@
 			want = filepath.Join(b.root, want)
 		}
 		if dir != want {
-			b.errs.AddNewf(token.NoPos,
+			err := errors.Newf(token.NoPos,
 				"file %s mapped to inconsistent path %s; module name %q may be inconsistent with root dir %s",
 				want, dir, b.module, b.root,
 			)
+			b.errs = errors.Append(b.errs, err)
 		}
 	}