Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
cue/errors: add Wrap
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mpvl committed Apr 20, 2021
1 parent 276ce26 commit dcb2a1f
Show file tree
Hide file tree
Showing 18 changed files with 113 additions and 52 deletions.
1 change: 1 addition & 0 deletions cmd/cue/cmd/testdata/script/cmd_err.txt
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions cmd/cue/cmd/testdata/script/cmd_errpos.txt
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions cmd/cue/cmd/testdata/script/export_err.txt
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions cmd/cue/cmd/testdata/script/vet_yaml.txt
Expand Up @@ -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"
Expand Down
96 changes: 66 additions & 30 deletions cue/errors/errors.go
Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions cue/testdata/eval/dynamic_field.txtar
Expand Up @@ -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:
(_|_){
Expand All @@ -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) }
}
}
Expand Down
4 changes: 4 additions & 0 deletions cue/testdata/fulleval/055_issue318.txtar
Expand Up @@ -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

Expand All @@ -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: (_|_){
Expand Down
3 changes: 3 additions & 0 deletions cue/testdata/interpolation/041_interpolation.txtar
Expand Up @@ -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:
(_|_){
Expand All @@ -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
}
}
1 change: 1 addition & 0 deletions cue/testdata/interpolation/incomplete.txtar
Expand Up @@ -27,6 +27,7 @@ out: """
out: (_|_){
// [incomplete] out: invalid interpolation: undefined field #d:
// ./in.cue:8:6
// ./in.cue:13:21
}
}
-- out/compile --
Expand Down
2 changes: 2 additions & 0 deletions cue/testdata/references/errors.txtar
Expand Up @@ -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
}
}
}
Expand Down
5 changes: 0 additions & 5 deletions internal/core/adt/errors.go
Expand Up @@ -211,7 +211,6 @@ type ValueError struct {
v *Vertex
pos token.Pos
auxpos []token.Pos
err errors.Error
errors.Message
}

Expand Down Expand Up @@ -325,7 +324,3 @@ func (e *ValueError) Path() (a []string) {
}
return a
}

func (e ValueError) Unwrap() error {
return e.err
}
3 changes: 1 addition & 2 deletions internal/core/adt/expr.go
Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions internal/task/task.go
Expand Up @@ -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
Expand All @@ -99,7 +98,6 @@ type taskError struct {
task cue.Value
v cue.Value
errors.Message
err error
}

var _ errors.Error = &taskError{}
Expand All @@ -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)

Expand Down
4 changes: 3 additions & 1 deletion pkg/encoding/json/testdata/gen.txtar
Expand Up @@ -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"
Expand Down
8 changes: 6 additions & 2 deletions pkg/encoding/yaml/testdata/gen.txtar
Expand Up @@ -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)
Expand Down
19 changes: 14 additions & 5 deletions pkg/list/testdata/gen.txtar
Expand Up @@ -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
Expand All @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion pkg/list/testdata/list.txtar
Expand Up @@ -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: {
Expand Down
3 changes: 2 additions & 1 deletion pkg/math/testdata/gen.txtar
Expand Up @@ -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

Expand Down

0 comments on commit dcb2a1f

Please sign in to comment.