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