diff --git a/cmd/cue/cmd/testdata/script/cmd_err.txt b/cmd/cue/cmd/testdata/script/cmd_err.txt index a3b31d868..3319baa7c 100644 --- a/cmd/cue/cmd/testdata/script/cmd_err.txt +++ b/cmd/cue/cmd/testdata/script/cmd_err.txt @@ -10,6 +10,7 @@ command.ref.task.display.filename: invalid string argument: non-concrete value s ./task_tool.cue:6:8 ./task_tool.cue:7:9 tool/file:15:3 + tool/file:15:16 -- task_tool.cue -- package home diff --git a/cmd/cue/cmd/testdata/script/cmd_errpos.txt b/cmd/cue/cmd/testdata/script/cmd_errpos.txt index 328fcac10..f2db94818 100644 --- a/cmd/cue/cmd/testdata/script/cmd_errpos.txt +++ b/cmd/cue/cmd/testdata/script/cmd_errpos.txt @@ -12,6 +12,7 @@ command.prompter.contents: invalid bytes argument: non-concrete value string: command.prompter.filename: invalid string argument: non-concrete value string: ./task_tool.cue:9:10 tool/file:9:3 + tool/file:9:16 -- task_tool.cue -- package foo diff --git a/cmd/cue/cmd/testdata/script/export_err.txt b/cmd/cue/cmd/testdata/script/export_err.txt index 6e6a41595..ae52f07b7 100644 --- a/cmd/cue/cmd/testdata/script/export_err.txt +++ b/cmd/cue/cmd/testdata/script/export_err.txt @@ -13,6 +13,7 @@ cmp stderr expect-stderr a.b.2.c: incomplete value int out: invalid interpolation: undefined field d: ./exporterr/export_err.cue:7:6 + ./exporterr/export_err.cue:7:16 -- expect-stdout -- -- exporterr/export_err.cue -- package exporterr diff --git a/cmd/cue/cmd/testdata/script/vet_yaml.txt b/cmd/cue/cmd/testdata/script/vet_yaml.txt index e61ed735c..0370ffdd6 100644 --- a/cmd/cue/cmd/testdata/script/vet_yaml.txt +++ b/cmd/cue/cmd/testdata/script/vet_yaml.txt @@ -4,6 +4,7 @@ cmp stderr expect-stderr -- expect-stderr -- phrases: invalid value "phrases:\n # A quote from Mark Twain.\n quote1:\n lang: en\n attribution: Mark Twain\n\n # A Norwegian proverb.\n proverb:\n lang: no\n text: Stemmen som sier at du ikke klarer det, lyver." (does not satisfy encoding/yaml.Validate({phrases:{},#Phrase:{lang:=~"^[a-zA-Z0-9-_]{2,}$" | false,text:!=""},#LanguageTag:=~"^[a-zA-Z0-9-_]{2,}$" | false})): error in call to encoding/yaml.Validate: incomplete value !="": ./yaml.cue:19:10 + ./yaml.cue:11:17 ./yaml.cue:21:10 -- yaml.cue -- import "encoding/yaml" diff --git a/cue/errors/errors.go b/cue/errors/errors.go index 20b5bd0fd..e8e5a0ec7 100644 --- a/cue/errors/errors.go +++ b/cue/errors/errors.go @@ -170,25 +170,77 @@ func Newf(p token.Pos, format string, args ...interface{}) Error { // Wrapf creates an Error with the associated position and message. The provided // error is added for inspection context. func Wrapf(err error, p token.Pos, format string, args ...interface{}) Error { - a, ok := err.(list) + pErr := &posError{ + pos: p, + Message: NewMessage(format, args), + } + return Wrap(pErr, err) +} + +// Wrap creates a new error where child is a subordinate error of parent. +// If child is list of Errors, the result will itself be a list of errors +// where child is a subordinate error of each parent. +func Wrap(parent Error, child error) Error { + if child == nil { + return parent + } + a, ok := child.(list) if !ok { - return &posError{ - pos: p, - Message: NewMessage(format, args), - err: err, - } + return &wrapped{parent, child} } - b := make([]Error, len(a)) + b := make(list, len(a)) for i, err := range a { - b[i] = &posError{ - pos: p, - Message: NewMessage(format, args), - err: err, - } + b[i] = &wrapped{parent, err} + } + return b +} + +type wrapped struct { + main Error + wrap error +} + +// Error implements the error interface. +func (e *wrapped) Error() string { + switch msg := e.main.Error(); { + case e.wrap == nil: + return msg + case msg == "": + return e.wrap.Error() + default: + return fmt.Sprintf("%s: %s", msg, e.wrap) + } +} + +func (e *wrapped) Msg() (format string, args []interface{}) { + return e.main.Msg() +} + +func (e *wrapped) Path() []string { + if p := Path(e.main); p != nil { + return p + } + return Path(e.wrap) +} + +func (e *wrapped) InputPositions() []token.Pos { + return append(e.main.InputPositions(), Positions(e.wrap)...) +} + +func (e *wrapped) Position() token.Pos { + if p := e.main.Position(); p != token.NoPos { + return p + } + if wrap, ok := e.wrap.(Error); ok { + return wrap.Position() } - return list(b) + return token.NoPos } +func (e *wrapped) Unwrap() error { return e.wrap } + +func (e *wrapped) Cause() error { return e.wrap } + // 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) { @@ -209,27 +261,11 @@ type posError struct { pos token.Pos inputs []token.Pos Message - - // The underlying error that triggered this one, if any. - err error } -func (e *posError) Path() []string { return Path(e.err) } +func (e *posError) Path() []string { return nil } func (e *posError) InputPositions() []token.Pos { return e.inputs } func (e *posError) Position() token.Pos { return e.pos } -func (e *posError) Unwrap() error { return e.err } -func (e *posError) Cause() error { return e.err } - -// Error implements the error interface. -func (e *posError) Error() string { - if e.err == nil { - return e.Message.Error() - } - if e.Message.format == "" { - return e.err.Error() - } - return fmt.Sprintf("%s: %s", e.Message.Error(), e.err) -} // Append combines two errors, flattening Lists as necessary. func Append(a, b Error) Error { diff --git a/cue/testdata/eval/dynamic_field.txtar b/cue/testdata/eval/dynamic_field.txtar index 003ee5ecc..b35259381 100644 --- a/cue/testdata/eval/dynamic_field.txtar +++ b/cue/testdata/eval/dynamic_field.txtar @@ -25,6 +25,8 @@ parenExprRefEqual: { Errors: invalid interpolation: conflicting values 2 and 1: ./in.cue:12:31 + ./in.cue:12:34 + ./in.cue:12:36 Result: (_|_){ @@ -40,6 +42,8 @@ Result: issue799: (_|_){ // [eval] invalid interpolation: conflicting values 2 and 1: // ./in.cue:12:31 + // ./in.cue:12:34 + // ./in.cue:12:36 key: (int){ &(>=-2147483648, <=2147483647, int) } } } diff --git a/cue/testdata/fulleval/055_issue318.txtar b/cue/testdata/fulleval/055_issue318.txtar index b43df7119..7b1383221 100644 --- a/cue/testdata/fulleval/055_issue318.txtar +++ b/cue/testdata/fulleval/055_issue318.txtar @@ -45,8 +45,10 @@ Errors: #T.out1: invalid interpolation: undefined field y: ./in.cue:3:8 + ./in.cue:3:24 #T.out2: invalid interpolation: undefined field y: ./in.cue:4:8 + ./in.cue:4:15 #T.vy: undefined field y: ./in.cue:6:12 @@ -61,10 +63,12 @@ Result: out1: (_|_){ // [eval] #T.out1: invalid interpolation: undefined field y: // ./in.cue:3:8 + // ./in.cue:3:24 } out2: (_|_){ // [eval] #T.out2: invalid interpolation: undefined field y: // ./in.cue:4:8 + // ./in.cue:4:15 } vx: (string){ string } vy: (_|_){ diff --git a/cue/testdata/interpolation/041_interpolation.txtar b/cue/testdata/interpolation/041_interpolation.txtar index 8f2dd9bd7..ea18b8e32 100644 --- a/cue/testdata/interpolation/041_interpolation.txtar +++ b/cue/testdata/interpolation/041_interpolation.txtar @@ -35,6 +35,7 @@ e: _|_ // expression in interpolation must evaluate to a number kind or string ( Errors: e: invalid interpolation: cannot use [] (type list) as type (bool|string|bytes|number): ./in.cue:7:4 + ./in.cue:7:7 Result: (_|_){ @@ -49,10 +50,12 @@ Result: u: (_|_){ // [incomplete] u: invalid interpolation: non-concrete value _ (type _): // ./in.cue:5:4 + // ./in.cue:5:7 } r: (_){ _ } e: (_|_){ // [eval] e: invalid interpolation: cannot use [] (type list) as type (bool|string|bytes|number): // ./in.cue:7:4 + // ./in.cue:7:7 } } diff --git a/cue/testdata/interpolation/incomplete.txtar b/cue/testdata/interpolation/incomplete.txtar index 29a5343ad..8d5290822 100644 --- a/cue/testdata/interpolation/incomplete.txtar +++ b/cue/testdata/interpolation/incomplete.txtar @@ -27,6 +27,7 @@ out: """ out: (_|_){ // [incomplete] out: invalid interpolation: undefined field #d: // ./in.cue:8:6 + // ./in.cue:13:21 } } -- out/compile -- diff --git a/cue/testdata/references/errors.txtar b/cue/testdata/references/errors.txtar index e33c5360a..4e33acf8c 100644 --- a/cue/testdata/references/errors.txtar +++ b/cue/testdata/references/errors.txtar @@ -80,10 +80,12 @@ Result: r1: (_|_){ // [incomplete] missingFieldNestedInInterpolation.r1: invalid interpolation: undefined field b: // ./references.cue:27:9 + // ./references.cue:27:14 } r2: (_|_){ // [incomplete] missingFieldNestedInInterpolation.r2: invalid interpolation: undefined field d: // ./references.cue:30:9 + // ./references.cue:30:14 } } } diff --git a/internal/core/adt/errors.go b/internal/core/adt/errors.go index c990092bd..20cfc6ddb 100644 --- a/internal/core/adt/errors.go +++ b/internal/core/adt/errors.go @@ -211,7 +211,6 @@ type ValueError struct { v *Vertex pos token.Pos auxpos []token.Pos - err errors.Error errors.Message } @@ -325,7 +324,3 @@ func (e *ValueError) Path() (a []string) { } return a } - -func (e ValueError) Unwrap() error { - return e.err -} diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go index 2e44ce883..714b8157e 100644 --- a/internal/core/adt/expr.go +++ b/internal/core/adt/expr.go @@ -1484,13 +1484,12 @@ func validateWithBuiltin(c *OpContext, src token.Pos, b *Builtin, args []Value) } vErr := c.NewPosf(src, "invalid value %s (does not satisfy %s)", args[0], buf.String()) - vErr.err = err for _, v := range args { vErr.AddPosition(v) } - return &Bottom{Code: severeness, Err: vErr} + return &Bottom{Code: severeness, Err: errors.Wrap(vErr, err)} } // A Disjunction represents a disjunction, where each disjunct may or may not diff --git a/internal/task/task.go b/internal/task/task.go index 1b43bb4fb..e8e585fd0 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -88,9 +88,8 @@ func (c *Context) addErr(v cue.Value, wrap error, format string, args ...interfa task: c.Obj, v: v, Message: errors.NewMessage(format, args), - err: wrap, } - c.Err = errors.Append(c.Err, err) + c.Err = errors.Append(c.Err, errors.Wrap(err, wrap)) } // taskError wraps some error values to retain position information about the @@ -99,7 +98,6 @@ type taskError struct { task cue.Value v cue.Value errors.Message - err error } var _ errors.Error = &taskError{} @@ -126,8 +124,6 @@ func (t *taskError) InputPositions() (a []token.Pos) { return a } -func (t *taskError) Unwrap() error { return t.err } - // A RunnerFunc creates a Runner. type RunnerFunc func(v cue.Value) (Runner, error) diff --git a/pkg/encoding/json/testdata/gen.txtar b/pkg/encoding/json/testdata/gen.txtar index f59b37927..f54b90098 100644 --- a/pkg/encoding/json/testdata/gen.txtar +++ b/pkg/encoding/json/testdata/gen.txtar @@ -17,7 +17,9 @@ t8: { t9: json.MarshalStream([{a: 1}, {b: int | *2}]) -- out/json -- Errors: -a: error in call to encoding/json.Validate: invalid value 10 (out of bound <3) +a: error in call to encoding/json.Validate: invalid value 10 (out of bound <3): + ./in.cue:4:36 + json.Validate:1:6 Result: import "encoding/json" diff --git a/pkg/encoding/yaml/testdata/gen.txtar b/pkg/encoding/yaml/testdata/gen.txtar index 034ef27e4..846b9a3b4 100644 --- a/pkg/encoding/yaml/testdata/gen.txtar +++ b/pkg/encoding/yaml/testdata/gen.txtar @@ -14,9 +14,13 @@ t8: yaml.Marshal({b: int | *2}) t9: yaml.MarshalStream([{a: 1}, {b: int | *2}]) -- out/yaml -- Errors: -a: error in call to encoding/yaml.Validate: invalid value 4 (out of bound <3) -a: error in call to encoding/yaml.ValidatePartial: invalid value 4 (out of bound <3) b: error in call to encoding/yaml.Validate: incomplete value int +a: error in call to encoding/yaml.Validate: invalid value 4 (out of bound <3): + ./in.cue:3:41 + yaml.Validate:3:5 +a: error in call to encoding/yaml.ValidatePartial: invalid value 4 (out of bound <3): + ./in.cue:6:48 + yaml.ValidatePartial:3:5 Result: t1: _|_ // error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3) (and 1 more errors) diff --git a/pkg/list/testdata/gen.txtar b/pkg/list/testdata/gen.txtar index 90269d4ce..bb1a9ac2c 100644 --- a/pkg/list/testdata/gen.txtar +++ b/pkg/list/testdata/gen.txtar @@ -65,7 +65,6 @@ t54: list.Sort([{a:1}, {b:2}], list.Ascending) Errors: error in call to list.Avg: empty list error in call to list.Drop: negative index -error in call to list.FlattenN: cannot use value "foo" (type string) as list error in call to list.Max: empty list error in call to list.Min: empty list error in call to list.Range: end must be greater than start when step is positive @@ -76,13 +75,23 @@ error in call to list.Slice: negative index error in call to list.Slice: slice bounds out of range error in call to list.Take: negative index Ascending.x: error in call to list.Sort: 2 errors in empty disjunction: -Ascending.x: error in call to list.Sort: conflicting values number and {b:2} (mismatched types number and struct) -Ascending.x: error in call to list.Sort: conflicting values string and {b:2} (mismatched types string and struct) +Ascending.x: error in call to list.Sort: conflicting values number and {b:2} (mismatched types number and struct): + ./in.cue:46:24 + list:10:9 +Ascending.x: error in call to list.Sort: conflicting values string and {b:2} (mismatched types string and struct): + ./in.cue:46:24 + list:10:18 Ascending.y: error in call to list.Sort: 2 errors in empty disjunction: -Ascending.y: error in call to list.Sort: conflicting values number and {a:1} (mismatched types number and struct) -Ascending.y: error in call to list.Sort: conflicting values string and {a:1} (mismatched types string and struct) +Ascending.y: error in call to list.Sort: conflicting values number and {a:1} (mismatched types number and struct): + ./in.cue:46:17 + list:10:9 +Ascending.y: error in call to list.Sort: conflicting values string and {a:1} (mismatched types string and struct): + ./in.cue:60:17 + list:10:18 t3: cannot use "foo" (type string) as list in argument 1 to list.Avg: ./in.cue:5:14 +error in call to list.FlattenN: cannot use value "foo" (type string) as list: + ./in.cue:15:20 t14: cannot use "foo" (type string) as int in argument 2 to list.FlattenN: ./in.cue:16:24 t17: cannot use "foo" (type string) as list in argument 1 to list.Max: diff --git a/pkg/list/testdata/list.txtar b/pkg/list/testdata/list.txtar index 05f94e45d..5ebcd3a7d 100644 --- a/pkg/list/testdata/list.txtar +++ b/pkg/list/testdata/list.txtar @@ -43,10 +43,11 @@ unique: { } -- out/list -- Errors: -error in call to list.Concat: cannot use value 1 (type int) as list error in call to list.Repeat: negative count concat.t7.v: cannot use 1 (type int) as list in argument 1 to list.Concat: ./in.cue:30:12 +error in call to list.Concat: cannot use value 1 (type int) as list: + ./in.cue:31:13 Result: repeat: { diff --git a/pkg/math/testdata/gen.txtar b/pkg/math/testdata/gen.txtar index 1c516007e..268c96bc8 100644 --- a/pkg/math/testdata/gen.txtar +++ b/pkg/math/testdata/gen.txtar @@ -25,9 +25,10 @@ t32: math.Dim(3, 2.5) t33: math.Dim(5, 7.2) -- out/math -- Errors: -error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000 t3: cannot call non-function math.Pi (type float): ./in.cue:5:5 +error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000: + ./in.cue:6:5 cannot use 2.0E+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up: ./in.cue:8:5