github.com/solo-io/cue@v0.4.7/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 "github.com/solo-io/cue/cue/ast" 36 "github.com/solo-io/cue/cue/errors" 37 cueformat "github.com/solo-io/cue/cue/format" 38 "github.com/solo-io/cue/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 int 44 45 const ( 46 // An EvalError is a fatal evaluation error. 47 EvalError ErrorCode = iota 48 49 // A UserError is a fatal error originating from the user. 50 UserError 51 52 // NotExistError is used to indicate a value does not exist. 53 // Mostly used for legacy reasons. 54 NotExistError 55 56 // StructuralCycleError means a structural cycle was found. Structural 57 // cycles are permanent errors, but they are not passed up recursively, 58 // as a unification of a value with a structural cycle with one that 59 // doesn't may still give a useful result. 60 StructuralCycleError 61 62 // IncompleteError means an evaluation could not complete because of 63 // insufficient information that may still be added later. 64 IncompleteError 65 66 // A CycleError indicates a reference error. It is considered to be 67 // an incomplete error, as reference errors may be broken by providing 68 // a concrete value. 69 CycleError 70 ) 71 72 func (c ErrorCode) String() string { 73 switch c { 74 case EvalError: 75 return "eval" 76 case UserError: 77 return "user" 78 case StructuralCycleError: 79 return "structural cycle" 80 case IncompleteError: 81 return "incomplete" 82 case CycleError: 83 return "cycle" 84 } 85 return "unknown" 86 } 87 88 // Bottom represents an error or bottom symbol. 89 // 90 // Although a Bottom node holds control data, it should not be created until the 91 // control information already resulted in an error. 92 type Bottom struct { 93 Src ast.Node 94 Err errors.Error 95 96 Code ErrorCode 97 HasRecursive bool 98 ChildError bool // Err is the error of the child 99 // Value holds the computed value so far in case 100 Value Value 101 } 102 103 func (x *Bottom) Source() ast.Node { return x.Src } 104 func (x *Bottom) Kind() Kind { return BottomKind } 105 func (x *Bottom) Specialize(k Kind) Value { return x } // XXX remove 106 107 func (b *Bottom) IsIncomplete() bool { 108 if b == nil { 109 return false 110 } 111 return b.Code == IncompleteError || b.Code == CycleError 112 } 113 114 // isLiteralBottom reports whether x is an error originating from a user. 115 func isLiteralBottom(x Expr) bool { 116 b, ok := x.(*Bottom) 117 return ok && b.Code == UserError 118 } 119 120 // isError reports whether v is an error or nil. 121 func isError(v Value) bool { 122 if v == nil { 123 return true 124 } 125 _, ok := v.(*Bottom) 126 return ok 127 } 128 129 // isIncomplete reports whether v is associated with an incomplete error. 130 func isIncomplete(v *Vertex) bool { 131 if v == nil { 132 return true 133 } 134 if b, ok := v.BaseValue.(*Bottom); ok { 135 return b.IsIncomplete() 136 } 137 return false 138 } 139 140 // AddChildError updates x to record an error that occurred in one of 141 // its descendent arcs. The resulting error will record the worst error code of 142 // the current error or recursive error. 143 // 144 // If x is not already an error, the value is recorded in the error for 145 // reference. 146 // 147 func (v *Vertex) AddChildError(recursive *Bottom) { 148 v.ChildErrors = CombineErrors(nil, v.ChildErrors, recursive) 149 if recursive.IsIncomplete() { 150 return 151 } 152 x := v.BaseValue 153 err, _ := x.(*Bottom) 154 if err == nil { 155 v.BaseValue = &Bottom{ 156 Code: recursive.Code, 157 Value: v, 158 HasRecursive: true, 159 ChildError: true, 160 Err: recursive.Err, 161 } 162 return 163 } 164 165 err.HasRecursive = true 166 if err.Code > recursive.Code { 167 err.Code = recursive.Code 168 } 169 170 v.BaseValue = err 171 } 172 173 // CombineErrors combines two errors that originate at the same Vertex. 174 func CombineErrors(src ast.Node, x, y Value) *Bottom { 175 a, _ := Unwrap(x).(*Bottom) 176 b, _ := Unwrap(y).(*Bottom) 177 178 if a == b && isCyclePlaceholder(a) { 179 return a 180 } 181 switch { 182 case a != nil && b != nil: 183 case a != nil: 184 return a 185 case b != nil: 186 return b 187 default: 188 return nil 189 } 190 191 if a.Code != b.Code { 192 if a.Code > b.Code { 193 a, b = b, a 194 } 195 196 if b.Code >= IncompleteError { 197 return a 198 } 199 } 200 201 return &Bottom{ 202 Src: src, 203 Err: errors.Append(a.Err, b.Err), 204 Code: a.Code, 205 } 206 } 207 208 // A ValueError is returned as a result of evaluating a value. 209 type ValueError struct { 210 r Runtime 211 v *Vertex 212 pos token.Pos 213 auxpos []token.Pos 214 errors.Message 215 } 216 217 func (v *ValueError) AddPosition(n Node) { 218 if n == nil { 219 return 220 } 221 if p := pos(n); p != token.NoPos { 222 for _, q := range v.auxpos { 223 if p == q { 224 return 225 } 226 } 227 v.auxpos = append(v.auxpos, p) 228 } 229 } 230 231 func (v *ValueError) AddClosedPositions(c CloseInfo) { 232 for s := c.closeInfo; s != nil; s = s.parent { 233 if loc := s.location; loc != nil { 234 v.AddPosition(loc) 235 } 236 } 237 } 238 239 func (c *OpContext) errNode() *Vertex { 240 return c.vertex 241 } 242 243 // MarkPositions marks the current position stack. 244 func (c *OpContext) MarkPositions() int { 245 return len(c.positions) 246 } 247 248 // ReleasePositions sets the position state to one from a call to MarkPositions. 249 func (c *OpContext) ReleasePositions(p int) { 250 c.positions = c.positions[:p] 251 } 252 253 func (c *OpContext) AddPosition(n Node) { 254 if n != nil { 255 c.positions = append(c.positions, n) 256 } 257 } 258 259 func (c *OpContext) Newf(format string, args ...interface{}) *ValueError { 260 return c.NewPosf(c.pos(), format, args...) 261 } 262 263 func appendNodePositions(a []token.Pos, n Node) []token.Pos { 264 if p := pos(n); p != token.NoPos { 265 a = append(a, p) 266 } else if v, ok := n.(*Vertex); ok { 267 for _, c := range v.Conjuncts { 268 a = appendNodePositions(a, c.x) 269 } 270 } 271 return a 272 } 273 274 func (c *OpContext) NewPosf(p token.Pos, format string, args ...interface{}) *ValueError { 275 var a []token.Pos 276 if len(c.positions) > 0 { 277 a = make([]token.Pos, 0, len(c.positions)) 278 for _, n := range c.positions { 279 a = appendNodePositions(a, n) 280 } 281 } 282 for i, arg := range args { 283 switch x := arg.(type) { 284 case Node: 285 a = appendNodePositions(a, x) 286 args[i] = c.Str(x) 287 case ast.Node: 288 b, _ := cueformat.Node(x) 289 if p := x.Pos(); p != token.NoPos { 290 a = append(a, p) 291 } 292 args[i] = string(b) 293 case Feature: 294 args[i] = x.SelectorString(c.Runtime) 295 } 296 } 297 return &ValueError{ 298 r: c.Runtime, 299 v: c.errNode(), 300 pos: p, 301 auxpos: a, 302 Message: errors.NewMessage(format, args), 303 } 304 } 305 306 func (e *ValueError) Error() string { 307 return errors.String(e) 308 } 309 310 func (e *ValueError) Position() token.Pos { 311 return e.pos 312 } 313 314 func (e *ValueError) InputPositions() (a []token.Pos) { 315 return e.auxpos 316 } 317 318 func (e *ValueError) Path() (a []string) { 319 if e.v == nil { 320 return nil 321 } 322 for _, f := range appendPath(nil, e.v) { 323 a = append(a, f.SelectorString(e.r)) 324 } 325 return a 326 }