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  }