cuelang.org/go@v0.13.0/internal/core/adt/errors.go (about) 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 15 package 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 34 import ( 35 "cuelang.org/go/cue/ast" 36 "cuelang.org/go/cue/errors" 37 cueformat "cuelang.org/go/cue/format" 38 "cuelang.org/go/cue/token" 39 ) 40 41 // ErrorCode indicates the type of error. The type of error may influence 42 // control flow. No other aspects of an error may influence control flow. 43 type ErrorCode int8 44 45 //go:generate go run golang.org/x/tools/cmd/stringer -type=ErrorCode -linecomment 46 47 const ( 48 // An EvalError is a fatal evaluation error. 49 EvalError ErrorCode = iota // eval 50 51 // A UserError is a fatal error originating from the user. 52 UserError // user 53 54 // StructuralCycleError means a structural cycle was found. Structural 55 // cycles are permanent errors, but they are not passed up recursively, 56 // as a unification of a value with a structural cycle with one that 57 // doesn't may still give a useful result. 58 StructuralCycleError // structural cycle 59 60 // IncompleteError means an evaluation could not complete because of 61 // insufficient information that may still be added later. 62 IncompleteError // incomplete 63 64 // A CycleError indicates a reference error. It is considered to be 65 // an incomplete error, as reference errors may be broken by providing 66 // a concrete value. 67 CycleError // cycle 68 ) 69 70 // Bottom represents an error or bottom symbol. 71 // 72 // Although a Bottom node holds control data, it should not be created until the 73 // control information already resulted in an error. 74 type Bottom struct { 75 Src ast.Node 76 Err errors.Error 77 78 Code ErrorCode 79 // Permanent indicates whether an incomplete error can be 80 // resolved later without making the configuration more specific. 81 // This may happen when an arc isn't fully resolved yet. 82 Permanent bool 83 HasRecursive bool 84 ChildError bool // Err is the error of the child 85 NotExists bool // This error originated from a failed lookup. 86 CloseCheck bool // This error resulted from a close check. 87 ForCycle bool // this is a for cycle 88 // Value holds the computed value so far in case 89 Value Value 90 91 // Node marks the node at which an error occurred. This is used to 92 // determine the package to which an error belongs. 93 // TODO: use a more precise mechanism for tracking the package. 94 Node *Vertex 95 } 96 97 func (x *Bottom) Source() ast.Node { return x.Src } 98 func (x *Bottom) Kind() Kind { return BottomKind } 99 func (x *Bottom) Specialize(k Kind) Value { return x } // XXX remove 100 101 func (b *Bottom) IsIncomplete() bool { 102 if b == nil { 103 return false 104 } 105 return b.Code == IncompleteError || b.Code == CycleError 106 } 107 108 // isLiteralBottom reports whether x is an error originating from a user. 109 func isLiteralBottom(x Expr) bool { 110 b, ok := x.(*Bottom) 111 return ok && b.Code == UserError 112 } 113 114 // isError reports whether v is an error or nil. 115 func isError(v Value) bool { 116 if v == nil { 117 return true 118 } 119 _, ok := v.(*Bottom) 120 return ok 121 } 122 123 // isIncomplete reports whether v is associated with an incomplete error. 124 func isIncomplete(v *Vertex) bool { 125 if v == nil { 126 return true 127 } 128 if b := v.Bottom(); b != nil { 129 return b.IsIncomplete() 130 } 131 return false 132 } 133 134 // AddChildError updates x to record an error that occurred in one of 135 // its descendent arcs. The resulting error will record the worst error code of 136 // the current error or recursive error. 137 // 138 // If x is not already an error, the value is recorded in the error for 139 // reference. 140 func (n *nodeContext) AddChildError(recursive *Bottom) { 141 v := n.node 142 v.ChildErrors = CombineErrors(nil, v.ChildErrors, recursive) 143 if recursive.IsIncomplete() { 144 return 145 } 146 x := v.BaseValue 147 err, _ := x.(*Bottom) 148 if err == nil || err.CloseCheck { 149 n.setBaseValue(&Bottom{ 150 Code: recursive.Code, 151 Value: v, 152 HasRecursive: true, 153 ChildError: true, 154 CloseCheck: recursive.CloseCheck, 155 Err: recursive.Err, 156 Node: n.node, 157 }) 158 return 159 } 160 161 err.HasRecursive = true 162 if err.Code > recursive.Code { 163 err.Code = recursive.Code 164 } 165 166 n.setBaseValue(err) 167 } 168 169 // CombineErrors combines two errors that originate at the same Vertex. 170 func CombineErrors(src ast.Node, x, y Value) *Bottom { 171 a, _ := Unwrap(x).(*Bottom) 172 b, _ := Unwrap(y).(*Bottom) 173 174 switch { 175 case a == nil && b == nil: 176 return nil 177 case a == nil: 178 return b 179 case b == nil: 180 return a 181 case a == b && isCyclePlaceholder(a): 182 return a 183 case a == b: 184 // Don't return a (or b) because they may have other non-nil fields. 185 return &Bottom{ 186 Src: src, 187 Err: a.Err, 188 Code: a.Code, 189 } 190 } 191 192 if a.Code != b.Code { 193 if a.Code > b.Code { 194 a, b = b, a 195 } 196 197 if b.Code >= IncompleteError { 198 return a 199 } 200 } 201 202 return &Bottom{ 203 Src: src, 204 Err: errors.Append(a.Err, b.Err), 205 Code: a.Code, 206 CloseCheck: a.CloseCheck || b.CloseCheck, 207 } 208 } 209 210 func addPositions(err *ValueError, c Conjunct) { 211 switch x := c.x.(type) { 212 case *Field: 213 // if x.ArcType == ArcRequired { 214 err.AddPosition(c.x) 215 // } 216 case *ConjunctGroup: 217 for _, c := range *x { 218 addPositions(err, c) 219 } 220 } 221 if c.CloseInfo.closeInfo != nil { 222 err.AddPosition(c.CloseInfo.location) 223 } 224 } 225 226 func NewRequiredNotPresentError(ctx *OpContext, v *Vertex) *Bottom { 227 saved := ctx.PushArc(v) 228 err := ctx.Newf("field is required but not present") 229 v.VisitLeafConjuncts(func(c Conjunct) bool { 230 if f, ok := c.x.(*Field); ok && f.ArcType == ArcRequired { 231 err.AddPosition(c.x) 232 } 233 if c.CloseInfo.closeInfo != nil { 234 err.AddPosition(c.CloseInfo.location) 235 } 236 return true 237 }) 238 239 b := &Bottom{ 240 Code: IncompleteError, 241 Err: err, 242 Node: v, 243 } 244 ctx.PopArc(saved) 245 return b 246 } 247 248 func newRequiredFieldInComprehensionError(ctx *OpContext, x *ForClause, v *Vertex) *Bottom { 249 err := ctx.Newf("missing required field in for comprehension: %v", v.Label) 250 err.AddPosition(x.Src) 251 v.VisitLeafConjuncts(func(c Conjunct) bool { 252 addPositions(err, c) 253 return true 254 }) 255 return &Bottom{ 256 Code: IncompleteError, 257 Err: err, 258 } 259 } 260 261 func (v *Vertex) reportFieldIndexError(c *OpContext, pos token.Pos, f Feature) { 262 v.reportFieldError(c, pos, f, 263 "index out of range [%d] with length %d", 264 "undefined field: %s") 265 } 266 267 func (v *Vertex) reportFieldCycleError(c *OpContext, pos token.Pos, f Feature) *Bottom { 268 const msg = "cyclic reference to field %[1]v" 269 b := v.reportFieldError(c, pos, f, msg, msg) 270 return b 271 } 272 273 func (v *Vertex) reportFieldError(c *OpContext, pos token.Pos, f Feature, intMsg, stringMsg string) *Bottom { 274 code := IncompleteError 275 if !v.Accept(c, f) { 276 code = EvalError 277 } 278 279 label := f.SelectorString(c.Runtime) 280 281 var err errors.Error 282 if f.IsInt() { 283 err = c.NewPosf(pos, intMsg, f.Index(), len(v.Elems())) 284 } else { 285 err = c.NewPosf(pos, stringMsg, label) 286 } 287 b := &Bottom{ 288 Code: code, 289 Err: err, 290 Node: v, 291 } 292 // TODO: yield failure 293 c.AddBottom(b) // TODO: unify error mechanism. 294 return b 295 } 296 297 // A ValueError is returned as a result of evaluating a value. 298 type ValueError struct { 299 r Runtime 300 v *Vertex 301 pos token.Pos 302 auxpos []token.Pos 303 altPath []string 304 errors.Message 305 } 306 307 func (v *ValueError) AddPosition(n Node) { 308 if n == nil { 309 return 310 } 311 if p := pos(n); p != token.NoPos { 312 for _, q := range v.auxpos { 313 if p == q { 314 return 315 } 316 } 317 v.auxpos = append(v.auxpos, p) 318 } 319 } 320 321 func (v *ValueError) AddClosedPositions(c CloseInfo) { 322 for s := c.closeInfo; s != nil; s = s.parent { 323 if loc := s.location; loc != nil { 324 v.AddPosition(loc) 325 } 326 } 327 } 328 329 func (c *OpContext) errNode() *Vertex { 330 return c.vertex 331 } 332 333 // MarkPositions marks the current position stack. 334 func (c *OpContext) MarkPositions() int { 335 return len(c.positions) 336 } 337 338 // ReleasePositions sets the position state to one from a call to MarkPositions. 339 func (c *OpContext) ReleasePositions(p int) { 340 c.positions = c.positions[:p] 341 } 342 343 func (c *OpContext) AddPosition(n Node) { 344 if n != nil { 345 c.positions = append(c.positions, n) 346 } 347 } 348 349 func (c *OpContext) Newf(format string, args ...interface{}) *ValueError { 350 return c.NewPosf(c.pos(), format, args...) 351 } 352 353 func appendNodePositions(a []token.Pos, n Node) []token.Pos { 354 if p := pos(n); p != token.NoPos { 355 a = append(a, p) 356 } 357 if v, ok := n.(*Vertex); ok { 358 v.VisitLeafConjuncts(func(c Conjunct) bool { 359 a = appendNodePositions(a, c.Elem()) 360 return true 361 }) 362 } 363 return a 364 } 365 366 func (c *OpContext) NewPosf(p token.Pos, format string, args ...interface{}) *ValueError { 367 var a []token.Pos 368 if len(c.positions) > 0 { 369 a = make([]token.Pos, 0, len(c.positions)) 370 for _, n := range c.positions { 371 a = appendNodePositions(a, n) 372 } 373 } 374 for i, arg := range args { 375 switch x := arg.(type) { 376 case Node: 377 a = appendNodePositions(a, x) 378 args[i] = c.Str(x) 379 case ast.Node: 380 // TODO: ideally the core evaluator should not depend on higher 381 // level packages. This will allow the debug packages to be used 382 // more widely. 383 b, _ := cueformat.Node(x) 384 if p := x.Pos(); p != token.NoPos { 385 a = append(a, p) 386 } 387 args[i] = string(b) 388 case Feature: 389 args[i] = x.SelectorString(c.Runtime) 390 } 391 } 392 393 return &ValueError{ 394 r: c.Runtime, 395 v: c.errNode(), 396 pos: p, 397 auxpos: a, 398 altPath: c.makeAltPath(), 399 Message: errors.NewMessagef(format, args...), 400 } 401 } 402 403 func (c *OpContext) makeAltPath() (a []string) { 404 if len(c.altPath) == 0 { 405 return nil 406 } 407 408 for _, f := range appendPath(nil, c.altPath[0]) { 409 a = append(a, f.SelectorString(c)) 410 } 411 for _, v := range c.altPath[1:] { 412 if f := v.Label; f != 0 { 413 a = append(a, f.SelectorString(c)) 414 } 415 } 416 return a 417 } 418 419 func (e *ValueError) Error() string { 420 return errors.String(e) 421 } 422 423 func (e *ValueError) Position() token.Pos { 424 return e.pos 425 } 426 427 func (e *ValueError) InputPositions() (a []token.Pos) { 428 return e.auxpos 429 } 430 431 func (e *ValueError) Path() (a []string) { 432 if len(e.altPath) > 0 { 433 return e.altPath 434 } 435 if e.v == nil { 436 return nil 437 } 438 for _, f := range appendPath(nil, e.v) { 439 a = append(a, f.SelectorString(e.r)) 440 } 441 return a 442 }