cue/errors: add Wrap
introduces new internal wrapping error that allows
harmonizing wrapped errors.
Issue #52
Change-Id: I93844d4cc7592521b70b6402a9670a941c2f6dfc
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9447
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/script/cmd_err.txt b/cmd/cue/cmd/testdata/script/cmd_err.txt
index a3b31d8..3319baa 100644
--- a/cmd/cue/cmd/testdata/script/cmd_err.txt
+++ b/cmd/cue/cmd/testdata/script/cmd_err.txt
@@ -10,6 +10,7 @@
./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 328fcac..f2db948 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.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 6e6a415..ae52f07 100644
--- a/cmd/cue/cmd/testdata/script/export_err.txt
+++ b/cmd/cue/cmd/testdata/script/export_err.txt
@@ -13,6 +13,7 @@
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 e61ed73..0370ffd 100644
--- a/cmd/cue/cmd/testdata/script/vet_yaml.txt
+++ b/cmd/cue/cmd/testdata/script/vet_yaml.txt
@@ -4,6 +4,7 @@
-- 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 20b5bd0..e8e5a0e 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -170,25 +170,77 @@
// 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)
- if !ok {
- return &posError{
- pos: p,
- Message: NewMessage(format, args),
- err: err,
- }
+ pErr := &posError{
+ pos: p,
+ Message: NewMessage(format, args),
}
- b := make([]Error, len(a))
- for i, err := range a {
- b[i] = &posError{
- pos: p,
- Message: NewMessage(format, args),
- err: err,
- }
- }
- return list(b)
+ 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 &wrapped{parent, child}
+ }
+ b := make(list, len(a))
+ for i, err := range a {
+ 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 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 @@
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 003ee5e..b352593 100644
--- a/cue/testdata/eval/dynamic_field.txtar
+++ b/cue/testdata/eval/dynamic_field.txtar
@@ -25,6 +25,8 @@
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 @@
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 b43df71..7b13832 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 @@
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 8f2dd9b..ea18b8e 100644
--- a/cue/testdata/interpolation/041_interpolation.txtar
+++ b/cue/testdata/interpolation/041_interpolation.txtar
@@ -35,6 +35,7 @@
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 @@
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 29a5343..8d52908 100644
--- a/cue/testdata/interpolation/incomplete.txtar
+++ b/cue/testdata/interpolation/incomplete.txtar
@@ -27,6 +27,7 @@
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 e33c536..4e33acf 100644
--- a/cue/testdata/references/errors.txtar
+++ b/cue/testdata/references/errors.txtar
@@ -80,10 +80,12 @@
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 c990092..20cfc6d 100644
--- a/internal/core/adt/errors.go
+++ b/internal/core/adt/errors.go
@@ -211,7 +211,6 @@
v *Vertex
pos token.Pos
auxpos []token.Pos
- err errors.Error
errors.Message
}
@@ -325,7 +324,3 @@
}
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 2e44ce8..714b815 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -1484,13 +1484,12 @@
}
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 1b43bb4..e8e585f 100644
--- a/internal/task/task.go
+++ b/internal/task/task.go
@@ -88,9 +88,8 @@
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 @@
task cue.Value
v cue.Value
errors.Message
- err error
}
var _ errors.Error = &taskError{}
@@ -126,8 +124,6 @@
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 f59b379..f54b900 100644
--- a/pkg/encoding/json/testdata/gen.txtar
+++ b/pkg/encoding/json/testdata/gen.txtar
@@ -17,7 +17,9 @@
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 034ef27..846b9a3 100644
--- a/pkg/encoding/yaml/testdata/gen.txtar
+++ b/pkg/encoding/yaml/testdata/gen.txtar
@@ -14,9 +14,13 @@
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 90269d4..bb1a9ac 100644
--- a/pkg/list/testdata/gen.txtar
+++ b/pkg/list/testdata/gen.txtar
@@ -65,7 +65,6 @@
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: 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 05f94e4..5ebcd3a 100644
--- a/pkg/list/testdata/list.txtar
+++ b/pkg/list/testdata/list.txtar
@@ -43,10 +43,11 @@
}
-- 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 1c51600..268c96b 100644
--- a/pkg/math/testdata/gen.txtar
+++ b/pkg/math/testdata/gen.txtar
@@ -25,9 +25,10 @@
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