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  }