Skip to content

Commit 05fbfe4

Browse files
committed
cue/errors: make List implement Error
The semants is for List to adopt the features of the first error. This is consistent with the default error message being printed. Advantages: - Allows improved type checking. - Avoids the nil-error return issues for List. - Simplifies combining code. Also: - get rid of E - Implement: Promote, Append - clean up code accordingly - tighten type checking Change-Id: Iea90ab001111d914aad93731d4ebb50e91865e1d Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2480 Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent 0a97533 commit 05fbfe4

File tree

8 files changed

+124
-130
lines changed

8 files changed

+124
-130
lines changed

cue/ast.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
//
3636
// There should be no unresolved identifiers in file, meaning the Node field
3737
// of all identifiers should be set to a non-nil value.
38-
func (inst *Instance) insertFile(f *ast.File) error {
38+
func (inst *Instance) insertFile(f *ast.File) errors.Error {
3939
// TODO: insert by converting to value first so that the trim command can
4040
// also remove top-level fields.
4141
// First process single file.
@@ -48,7 +48,8 @@ func (inst *Instance) insertFile(f *ast.File) error {
4848
if v.errors.Len() > 0 {
4949
return v.errors
5050
}
51-
return &callError{result.(*bottom)}
51+
v := newValueRoot(v.ctx(), result)
52+
return v.toErr(result.(*bottom))
5253
}
5354

5455
return nil

cue/build/import.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ import (
2626

2727
type LoadFunc func(pos token.Pos, path string) *Instance
2828

29+
type cueError = errors.Error
30+
31+
type buildError struct {
32+
cueError
33+
inputs []token.Pos
34+
}
35+
36+
func (e *buildError) InputPositions() []token.Pos {
37+
return e.inputs
38+
}
39+
2940
func (inst *Instance) complete() errors.Error {
3041
// TODO: handle case-insensitive collisions.
3142
// dir := inst.Dir
@@ -65,7 +76,10 @@ func (inst *Instance) complete() errors.Error {
6576
for path := range imported {
6677
paths = append(paths, path)
6778
if path == "" {
68-
return errors.E(imported[path], "empty import path")
79+
return &buildError{
80+
errors.Newf(token.NoPos, "empty import path"),
81+
imported[path],
82+
}
6983
}
7084
}
7185

cue/build/instance.go

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type Instance struct {
5959
// The Err for loading this package or nil on success. This does not
6060
// include any errors of dependencies. Incomplete will be set if there
6161
// were any errors in dependencies.
62-
Err error
62+
Err errors.Error
6363

6464
// Incomplete reports whether any dependencies had an error.
6565
Incomplete bool
@@ -125,22 +125,7 @@ func (inst *Instance) setPkg(pkg string) bool {
125125

126126
// ReportError reports an error processing this instance.
127127
func (inst *Instance) ReportError(err errors.Error) {
128-
var list errors.List
129-
switch x := inst.Err.(type) {
130-
default:
131-
// Should never happen, but in the worst case at least one error is
132-
// recorded.
133-
return
134-
case nil:
135-
inst.Err = err
136-
return
137-
case errors.List:
138-
list = x
139-
case errors.Error:
140-
list.Add(x)
141-
}
142-
list.Add(err)
143-
inst.Err = list
128+
inst.Err = errors.Append(inst.Err, err)
144129
}
145130

146131
// Context defines the build context for this instance. All files defined
@@ -181,18 +166,8 @@ func (inst *Instance) AddFile(filename string, src interface{}) error {
181166
file, err := parser.ParseFile(filename, src, parser.ParseComments)
182167
if err != nil {
183168
// should always be an errors.List, but just in case.
184-
switch x := err.(type) {
185-
case errors.List:
186-
for _, e := range x {
187-
inst.ReportError(e)
188-
}
189-
case errors.Error:
190-
inst.ReportError(x)
191-
default:
192-
e := errors.Wrapf(err, token.NoPos, "error adding file")
193-
inst.ReportError(e)
194-
err = e
195-
}
169+
err := errors.Promote(err, "error adding file")
170+
inst.ReportError(err)
196171
return err
197172
}
198173

cue/errors/errors.go

Lines changed: 94 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -148,31 +148,14 @@ func Wrapf(err error, p token.Pos, format string, args ...interface{}) Error {
148148
}
149149
}
150150

151-
// E creates a new error. The following types correspond to the following
152-
// methods:
153-
// arg type maps to
154-
// Message Msg
155-
// string shorthand for a format message with no arguments
156-
// token.Pos Position
157-
// []token.Pos Positions
158-
// error Unwrap/Cause
159-
func E(args ...interface{}) Error {
160-
e := &posError{}
161-
for _, a := range args {
162-
switch x := a.(type) {
163-
case string:
164-
e.Message = NewMessage(x, nil)
165-
case Message:
166-
e.Message = x
167-
case token.Pos:
168-
e.pos = x
169-
case []token.Pos:
170-
e.inputs = x
171-
case error:
172-
e.err = x
173-
}
151+
// Promote converts a regular Go error to an Error if it isn't already so.
152+
func Promote(err error, msg string) Error {
153+
switch x := err.(type) {
154+
case Error:
155+
return x
156+
default:
157+
return Wrapf(err, token.NoPos, msg)
174158
}
175-
return e
176159
}
177160

178161
var _ Error = &posError{}
@@ -201,25 +184,70 @@ func (e *posError) Error() string {
201184
if e.err == nil {
202185
return e.Message.Error()
203186
}
187+
if e.Message.format == "" {
188+
return e.err.Error()
189+
}
204190
return fmt.Sprintf("%s: %s", e.Message.Error(), e.err)
205191
}
206192

193+
// Append combines two errors, flattening Lists as necessary.
194+
func Append(a, b Error) Error {
195+
switch x := a.(type) {
196+
case nil:
197+
return b
198+
case List:
199+
return appendToList(x, b)
200+
}
201+
// Preserve order of errors.
202+
list := appendToList(nil, a)
203+
list = appendToList(list, b)
204+
return list
205+
}
206+
207+
// Errors reports the individual errors associated with an error, which is
208+
// the error itself if there is only one or, if the underlying type is List,
209+
// its individual elements. If the given error is not an Error, it will be
210+
// promoted to one.
211+
func Errors(err error) []Error {
212+
switch x := err.(type) {
213+
case nil:
214+
return nil
215+
case List:
216+
return []Error(x)
217+
case Error:
218+
return []Error{x}
219+
default:
220+
return []Error{Promote(err, "")}
221+
}
222+
}
223+
224+
func appendToList(list List, err Error) List {
225+
switch x := err.(type) {
226+
case nil:
227+
return list
228+
case List:
229+
if list == nil {
230+
return x
231+
}
232+
return append(list, x...)
233+
default:
234+
return append(list, err)
235+
}
236+
}
237+
207238
// List is a list of Errors.
208239
// The zero value for an List is an empty List ready to use.
209240
type List []Error
210241

211-
func (p *List) add(err Error) {
212-
*p = append(*p, err)
213-
}
214-
215242
// AddNewf adds an Error with given position and error message to an List.
216243
func (p *List) AddNewf(pos token.Pos, msg string, args ...interface{}) {
217-
p.add(&posError{pos: pos, Message: Message{format: msg, args: args}})
244+
err := &posError{pos: pos, Message: Message{format: msg, args: args}}
245+
*p = append(*p, err)
218246
}
219247

220248
// Add adds an Error with given position and error message to an List.
221249
func (p *List) Add(err Error) {
222-
p.add(err)
250+
*p = appendToList(*p, err)
223251
}
224252

225253
// Reset resets an List to no errors.
@@ -316,13 +344,44 @@ func (p *List) RemoveMultiples() {
316344

317345
// An List implements the error interface.
318346
func (p List) Error() string {
347+
format, args := p.Msg()
348+
return fmt.Sprintf(format, args...)
349+
}
350+
351+
// Msg reports the unformatted error message for the first error, if any.
352+
func (p List) Msg() (format string, args []interface{}) {
319353
switch len(p) {
320354
case 0:
321-
return "no errors"
355+
return "no errors", nil
322356
case 1:
323-
return p[0].Error()
357+
return p[0].Msg()
324358
}
325-
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
359+
format, args = p[0].Msg()
360+
return "%s (and %d more errors)", append(args, len(p)-1)
361+
}
362+
363+
// Position reports the primary position for the first error, if any.
364+
func (p List) Position() token.Pos {
365+
if len(p) == 0 {
366+
return token.NoPos
367+
}
368+
return p[0].Position()
369+
}
370+
371+
// InputPositions reports the input positions for the first error, if any.
372+
func (p List) InputPositions() []token.Pos {
373+
if len(p) == 0 {
374+
return nil
375+
}
376+
return p[0].InputPositions()
377+
}
378+
379+
// Path reports the path location of the first error, if any.
380+
func (p List) Path() []string {
381+
if len(p) == 0 {
382+
return nil
383+
}
384+
return p[0].Path()
326385
}
327386

328387
// Err returns an error equivalent to this error list.
@@ -356,12 +415,8 @@ func Print(w io.Writer, err error, cfg *Config) {
356415
if cfg == nil {
357416
cfg = &Config{}
358417
}
359-
if list, ok := err.(List); ok {
360-
for _, e := range list {
361-
printError(w, e, cfg)
362-
}
363-
} else if err != nil {
364-
printError(w, err, cfg)
418+
for _, e := range Errors(err) {
419+
printError(w, e, cfg)
365420
}
366421
}
367422

cue/instance.go

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"cuelang.org/go/cue/ast"
2121
"cuelang.org/go/cue/build"
2222
"cuelang.org/go/cue/errors"
23-
"cuelang.org/go/cue/token"
2423
"cuelang.org/go/internal"
2524
)
2625

@@ -40,8 +39,8 @@ type Instance struct {
4039
Dir string
4140
Name string
4241

43-
Incomplete bool // true if Pkg and all its dependencies are free of errors
44-
Err error // non-nil if the package had errors
42+
Incomplete bool // true if Pkg and all its dependencies are free of errors
43+
Err errors.Error // non-nil if the package had errors
4544

4645
inst *build.Instance
4746

@@ -84,41 +83,14 @@ func (x *index) NewInstance(p *build.Instance) *Instance {
8483
return i
8584
}
8685

87-
func (inst *Instance) setListOrError(err error) {
86+
func (inst *Instance) setListOrError(err errors.Error) {
8887
inst.Incomplete = true
89-
switch x := err.(type) {
90-
case errors.List:
91-
if inst.Err == nil {
92-
inst.Err = x
93-
return
94-
}
95-
for _, e := range x {
96-
inst.setError(e)
97-
}
98-
case errors.Error:
99-
inst.setError(x)
100-
default:
101-
inst.setError(errors.Wrapf(err, token.NoPos, "unknown error"))
102-
}
88+
inst.Err = errors.Append(inst.Err, err)
10389
}
10490

10591
func (inst *Instance) setError(err errors.Error) {
10692
inst.Incomplete = true
107-
var list errors.List
108-
switch x := inst.Err.(type) {
109-
default:
110-
// Should never happen, but in the worst case at least one error is
111-
// recorded.
112-
return
113-
case nil:
114-
inst.Err = err
115-
return
116-
case errors.List:
117-
list = x
118-
case errors.Error:
119-
list.Add(err)
120-
}
121-
inst.Err = list
93+
inst.Err = errors.Append(inst.Err, err)
12294
}
12395

12496
func (inst *Instance) eval(ctx *context) evaluated {

cue/load/config.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,7 @@ func (c Config) newInstance(path string) *build.Instance {
144144
func (c Config) newErrInstance(m *match, path string, err error) *build.Instance {
145145
i := c.Context.NewInstance(path, nil)
146146
i.DisplayPath = path
147-
switch x := err.(type) {
148-
case errors.Error:
149-
i.ReportError(x)
150-
case errors.List:
151-
for _, e := range x {
152-
i.ReportError(e)
153-
}
154-
default:
155-
i.ReportError(errors.Wrapf(err, token.NoPos, "instance"))
156-
}
147+
i.ReportError(errors.Promote(err, "instance"))
157148
return i
158149
}
159150

cue/load/import.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -358,16 +358,7 @@ func (fp *fileProcessor) add(pos token.Pos, root, path string, mode importMode)
358358
pf, perr := parser.ParseFile(filename, data, parser.ImportsOnly, parser.ParseComments)
359359
if perr != nil {
360360
// should always be an errors.List, but just in case.
361-
switch x := perr.(type) {
362-
case errors.List:
363-
for _, e := range x {
364-
badFile(e)
365-
}
366-
case errors.Error:
367-
badFile(x)
368-
default:
369-
badFile(errors.Wrapf(err, token.NoPos, "add failed"))
370-
}
361+
badFile(errors.Promote(perr, "add failed"))
371362
return true
372363
}
373364

encoding/protobuf/protobuf.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,7 @@ func (b *Extractor) Err() error {
118118
}
119119

120120
func (b *Extractor) addErr(err error) {
121-
switch err := err.(type) {
122-
case errors.Error:
123-
b.errs.Add(err)
124-
default:
125-
b.errs.AddNewf(token.NoPos, "unknown error: %v", err)
126-
}
121+
b.errs.Add(errors.Promote(err, "unknown error"))
127122
}
128123

129124
// AddFile adds a proto definition file to be converted into CUE by the builder.

0 commit comments

Comments
 (0)