1// Copyright 2020 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package adt
16
17// This file contains error encodings.
18//
19//
20// *Bottom:
21// - an adt.Value
22// - always belongs to a single vertex.
23// - does NOT implement error
24// - marks error code used for control flow
25//
26// errors.Error
27// - CUE default error
28// - implements error
29// - tracks error locations
30// - has error message details
31// - supports multiple errors
32//
33
34import (
35 "slices"
36
37 "cuelang.org/go/cue/ast"
38 "cuelang.org/go/cue/errors"
39 cueformat "cuelang.org/go/cue/format"
40 "cuelang.org/go/cue/token"
41 "cuelang.org/go/internal/iterutil"
42)
43
44// ErrorCode indicates the type of error. The type of error may influence
45// control flow. No other aspects of an error may influence control flow.
46type ErrorCode int8
47
48//go:generate go tool stringer -type=ErrorCode -linecomment
49
50const (
51 // An EvalError is a fatal evaluation error.
52 EvalError ErrorCode = iota // eval
53
54 // A UserError is a fatal error originating from the user using the error
55 // builtin.
56 UserError // user
57
58 // A LegacyUserError is a fatal error originating from the user using the
59 // _|_ token, which we intend to phase out.
60 LegacyUserError // user
61
62 // StructuralCycleError means a structural cycle was found. Structural
63 // cycles are permanent errors, but they are not passed up recursively,
64 // as a unification of a value with a structural cycle with one that
65 // doesn't may still give a useful result.
66 StructuralCycleError // structural cycle
67
68 // IncompleteError means an evaluation could not complete because of
69 // insufficient information that may still be added later.
70 IncompleteError // incomplete
71
72 // A CycleError indicates a reference error. It is considered to be
73 // an incomplete error, as reference errors may be broken by providing
74 // a concrete value.
75 CycleError // cycle
76)
77
78// Bottom represents an error or bottom symbol.
79//
80// Although a Bottom node holds control data, it should not be created until the
81// control information already resulted in an error.
82type Bottom struct {
83 Src ast.Node
84 Err errors.Error
85
86 Code ErrorCode
87 // Permanent indicates whether an incomplete error can be
88 // resolved later without making the configuration more specific.
89 // This may happen when an arc isn't fully resolved yet.
90 Permanent bool
91 HasRecursive bool
92 ChildError bool // Err is the error of the child
93 NotExists bool // This error originated from a failed lookup.
94 CloseCheck bool // This error resulted from a close check.
95 ForCycle bool // this is a for cycle
96 // Value holds the computed value so far in case
97 Value Value
98
99 // Node marks the node at which an error occurred. This is used to
100 // determine the package to which an error belongs.
101 // TODO: use a more precise mechanism for tracking the package.
102 Node *Vertex
103}
104
105func (x *Bottom) Source() ast.Node { return x.Src }
106func (x *Bottom) Kind() Kind { return BottomKind }
107
108func (b *Bottom) IsIncomplete() bool {
109 if b == nil {
110 return false
111 }
112 return b.Code == IncompleteError || b.Code == CycleError
113}
114
115// isLiteralBottom reports whether x is an error originating from a user.
116func isLiteralBottom(x Expr) bool {
117 b, ok := x.(*Bottom)
118 return ok && b.Code == LegacyUserError
119}
120
121// isError reports whether v is an error or nil.
122func isError(v Value) bool {
123 if v == nil {
124 return true
125 }
126 _, ok := v.(*Bottom)
127 return ok
128}
129
130// isIncomplete reports whether v is associated with an incomplete error.
131func isIncomplete(v *Vertex) bool {
132 if v == nil {
133 return true
134 }
135 if b := v.Bottom(); b != nil {
136 return b.IsIncomplete()
137 }
138 return false
139}
140
141// AddChildError updates x to record an error that occurred in one of
142// its descendent arcs. The resulting error will record the worst error code of
143// the current error or recursive error.
144//
145// If x is not already an error, the value is recorded in the error for
146// reference.
147func (n *nodeContext) AddChildError(recursive *Bottom) {
148 v := n.node
149 v.ChildErrors = CombineErrors(nil, v.ChildErrors, recursive)
150 if recursive.IsIncomplete() {
151 return
152 }
153 x := v.BaseValue
154 err, _ := x.(*Bottom)
155 if err == nil || err.CloseCheck {
156 n.setBaseValue(&Bottom{
157 Code: recursive.Code,
158 Value: v,
159 HasRecursive: true,
160 ChildError: true,
161 CloseCheck: recursive.CloseCheck,
162 Err: recursive.Err,
163 Node: n.node,
164 })
165 return
166 }
167
168 err.HasRecursive = true
169 if err.Code > recursive.Code {
170 err.Code = recursive.Code
171 }
172
173 n.setBaseValue(err)
174}
175
176// CombineErrors combines two errors that originate at the same Vertex.
177func CombineErrors(src ast.Node, x, y Value) *Bottom {
178 a, _ := Unwrap(x).(*Bottom)
179 b, _ := Unwrap(y).(*Bottom)
180
181 switch {
182 case a == nil && b == nil:
183 return nil
184 case a == nil:
185 return b
186 case b == nil:
187 return a
188 case a == b && isCyclePlaceholder(a):
189 return a
190 case a == b:
191 // Don't return a (or b) because they may have other non-nil fields.
192 return &Bottom{
193 Src: src,
194 Err: a.Err,
195 Code: a.Code,
196 }
197 }
198
199 if a.Code != b.Code {
200 if a.Code > b.Code {
201 a, b = b, a
202 }
203
204 if b.Code >= IncompleteError {
205 return a
206 }
207 }
208
209 return &Bottom{
210 Src: src,
211 Err: errors.Append(a.Err, b.Err),
212 Code: a.Code,
213 CloseCheck: a.CloseCheck || b.CloseCheck,
214 }
215}
216
217func addPositions(ctx *OpContext, err *ValueError, c Conjunct) {
218 switch x := c.x.(type) {
219 case *Field:
220 // if x.ArcType == ArcRequired {
221 err.AddPosition(c.x)
222 // }
223 case *ConjunctGroup:
224 for _, c := range *x {
225 addPositions(ctx, err, c)
226 }
227 }
228 err.AddPos(c.CloseInfo.Location(ctx))
229}
230
231func NewRequiredNotPresentError(ctx *OpContext, v *Vertex, morePositions ...Node) *Bottom {
232 saved := ctx.PushArc(v)
233 err := ctx.Newf("field is required but not present")
234 for _, p := range morePositions {
235 err.AddPosition(p)
236 }
237 for c := range v.LeafConjuncts() {
238 if f, ok := c.x.(*Field); ok && f.ArcType == ArcRequired {
239 err.AddPosition(c.x)
240 }
241 err.AddPos(c.CloseInfo.Location(ctx))
242 }
243
244 b := &Bottom{
245 Code: IncompleteError,
246 Err: err,
247 Node: v,
248 }
249 ctx.PopArc(saved)
250 return b
251}
252
253func newRequiredFieldInComprehensionError(ctx *OpContext, x *ForClause, v *Vertex) *Bottom {
254 err := ctx.Newf("missing required field in for comprehension: %v", v.Label)
255 err.AddPosition(x.Src)
256 for c := range v.LeafConjuncts() {
257 addPositions(ctx, err, c)
258 }
259 return &Bottom{
260 Code: IncompleteError,
261 Err: err,
262 }
263}
264
265func (v *Vertex) reportFieldIndexError(c *OpContext, pos token.Pos, f Feature) {
266 v.reportFieldError(c, pos, f,
267 "index out of range [%d] with length %d",
268 "undefined field: %s")
269}
270
271func (v *Vertex) reportFieldCycleError(c *OpContext, pos token.Pos, f Feature) *Bottom {
272 const msg = "cyclic reference to field %[1]v"
273 b := v.reportFieldError(c, pos, f, msg, msg)
274 return b
275}
276
277func (v *Vertex) reportFieldError(c *OpContext, pos token.Pos, f Feature, intMsg, stringMsg string) *Bottom {
278 code := IncompleteError
279 // If v is an error, we need to adopt the worst error.
280 if b := v.Bottom(); b != nil && !isCyclePlaceholder(b) {
281 code = b.Code
282 } else if !v.Accept(c, f) {
283 code = EvalError
284 }
285
286 label := f.SelectorString(c.Runtime)
287
288 var err errors.Error
289 if f.IsInt() {
290 err = c.NewPosf(pos, intMsg, f.Index(), iterutil.Count(v.Elems()))
291 } else {
292 err = c.NewPosf(pos, stringMsg, label)
293 }
294 b := &Bottom{
295 Code: code,
296 Err: err,
297 Node: v,
298 }
299 // TODO: yield failure
300 c.AddBottom(b) // TODO: unify error mechanism.
301 return b
302}
303
304// baseError contains common fields and methods for error types.
305type baseError struct {
306 r Runtime
307 v *Vertex
308 pos token.Pos
309 auxpos []token.Pos
310 altPath []string
311}
312
313func (e *baseError) AddPos(p token.Pos) {
314 if !p.IsValid() {
315 return
316 }
317 if slices.Contains(e.auxpos, p) {
318 return
319 }
320 e.auxpos = append(e.auxpos, p)
321}
322
323func (e *baseError) AddClosedPositions(ctx *OpContext, p posInfo) {
324 for n := range p.AncestorPositions(ctx) {
325 e.AddPos(n)
326 }
327}
328
329func (e *baseError) Position() token.Pos {
330 return e.pos
331}
332
333func (e *baseError) InputPositions() []token.Pos {
334 return e.auxpos
335}
336
337func (e *baseError) Path() (a []string) {
338 if len(e.altPath) > 0 {
339 return e.altPath
340 }
341 if e.v == nil {
342 return nil
343 }
344 for _, f := range appendPath(nil, e.v) {
345 a = append(a, f.SelectorString(e.r))
346 }
347 return a
348}
349
350// A ValueError is returned as a result of evaluating a value.
351type ValueError struct {
352 baseError
353 errors.Message
354}
355
356func (v *ValueError) AddPosition(n Node) {
357 v.AddPos(Pos(n))
358}
359
360func (c *OpContext) errNode() *Vertex {
361 return c.vertex
362}
363
364// MarkPositions marks the current position stack.
365func (c *OpContext) MarkPositions() int {
366 return len(c.positions)
367}
368
369// ReleasePositions sets the position state to one from a call to MarkPositions.
370func (c *OpContext) ReleasePositions(p int) {
371 c.positions = c.positions[:p]
372}
373
374func (c *OpContext) AddPosition(n Node) {
375 if n != nil {
376 c.positions = append(c.positions, n)
377 }
378}
379
380func (c *OpContext) Newf(format string, args ...interface{}) *ValueError {
381 return c.NewPosf(c.pos(), format, args...)
382}
383
384func appendNodePositions(a []token.Pos, n Node) []token.Pos {
385 if p := Pos(n); p.IsValid() {
386 a = append(a, p)
387 }
388 if v, ok := n.(*Vertex); ok {
389 for c := range v.LeafConjuncts() {
390 a = appendNodePositions(a, c.Elem())
391 }
392 }
393 return a
394}
395
396func (c *OpContext) NewPosf(p token.Pos, format string, args ...interface{}) *ValueError {
397 var a []token.Pos
398 if len(c.positions) > 0 {
399 a = make([]token.Pos, 0, len(c.positions))
400 for _, n := range c.positions {
401 a = appendNodePositions(a, n)
402 }
403 }
404 for i, arg := range args {
405 switch x := arg.(type) {
406 case Node:
407 a = appendNodePositions(a, x)
408 // Wrap nodes in a [fmt.Stringer] which delays the call to
409 // [OpContext.Str] until the error needs to be rendered.
410 // This helps avoid work, as in many cases,
411 // errors are created but never shown to the user.
412 //
413 // A Vertex will set an error as its BaseValue via a Bottom node,
414 // which might be this error we are creating.
415 // Using the Vertex directly could then lead to endless recursion.
416 // Make a shallow copy to avoid that.
417 if v, ok := x.(*Vertex); ok {
418 // TODO(perf): we could join this allocation with the creation
419 // of the stringer below.
420 vcopy := *v
421 x = &vcopy
422 }
423 args[i] = c.Str(x)
424 case ast.Node:
425 // TODO: ideally the core evaluator should not depend on higher
426 // level packages. This will allow the debug packages to be used
427 // more widely.
428 b, _ := cueformat.Node(x)
429 if p := x.Pos(); p.IsValid() {
430 a = append(a, p)
431 }
432 args[i] = string(b)
433 case Feature:
434 args[i] = x.SelectorString(c.Runtime)
435 }
436 }
437
438 return &ValueError{
439 baseError: baseError{
440 r: c.Runtime,
441 v: c.errNode(),
442 pos: p,
443 auxpos: a,
444 altPath: c.makeAltPath(),
445 },
446 Message: errors.NewMessagef(format, args...),
447 }
448}
449
450func (c *OpContext) makeAltPath() (a []string) {
451 if len(c.altPath) == 0 {
452 return nil
453 }
454
455 for _, f := range appendPath(nil, c.altPath[0]) {
456 a = append(a, f.SelectorString(c))
457 }
458 for _, v := range c.altPath[1:] {
459 if f := v.Label; f != 0 {
460 a = append(a, f.SelectorString(c))
461 }
462 }
463 return a
464}
465
466func (e *ValueError) Error() string {
467 return errors.String(e)
468}
469
470// ConflictError defers formatting of conflict messages until the error is
471// actually needed, avoiding expensive string conversions and allocations.
472type ConflictError struct {
473 baseError
474 format func(Runtime, Node) string
475 v1, v2 Node
476 k1, k2 Kind
477}
478
479func (e *ConflictError) Error() string {
480 return errors.String(e)
481}
482
483func (e *ConflictError) Msg() (format string, args []interface{}) {
484 v1Str := Formatter{X: e.v1, F: e.format, R: e.r}
485 v2Str := Formatter{X: e.v2, F: e.format, R: e.r}
486 if e.k1 == e.k2 {
487 return "conflicting values %s and %s", []interface{}{v1Str, v2Str}
488 }
489 return "conflicting values %s and %s (mismatched types %s and %s)",
490 []interface{}{v1Str, v2Str, e.k1, e.k2}
491}