this repo has no description
at master 491 lines 12 kB view raw
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}