cuelang.org/go@v0.13.0/internal/core/validate/validate.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 validate collects errors from an evaluated Vertex.
    16  package validate
    17  
    18  import (
    19  	"cuelang.org/go/internal/core/adt"
    20  )
    21  
    22  type Config struct {
    23  	// Concrete, if true, requires that all values be concrete.
    24  	Concrete bool
    25  
    26  	// Final, if true, checks that there are no required fields left.
    27  	Final bool
    28  
    29  	// DisallowCycles indicates that there may not be cycles.
    30  	DisallowCycles bool
    31  
    32  	// AllErrors continues descending into a Vertex, even if errors are found.
    33  	AllErrors bool
    34  
    35  	// TODO: omitOptional, if this is becomes relevant.
    36  }
    37  
    38  // Validate checks that a value has certain properties. The value must have
    39  // been evaluated.
    40  func Validate(ctx *adt.OpContext, v *adt.Vertex, cfg *Config) *adt.Bottom {
    41  	if cfg == nil {
    42  		cfg = &Config{}
    43  	}
    44  	x := validator{Config: *cfg, ctx: ctx}
    45  	x.validate(v)
    46  	return x.err
    47  }
    48  
    49  type validator struct {
    50  	Config
    51  	ctx          *adt.OpContext
    52  	err          *adt.Bottom
    53  	inDefinition int
    54  
    55  	// shared vertices should be visited at least once if referenced by
    56  	// a non-definition.
    57  	// TODO: we could also keep track of the number of references to a
    58  	// shared vertex. This would allow us to report more than a single error
    59  	// per shared vertex.
    60  	visited map[*adt.Vertex]bool
    61  }
    62  
    63  func (v *validator) checkConcrete() bool {
    64  	return v.Concrete && v.inDefinition == 0
    65  }
    66  
    67  func (v *validator) checkFinal() bool {
    68  	return (v.Concrete || v.Final) && v.inDefinition == 0
    69  }
    70  
    71  func (v *validator) add(b *adt.Bottom) {
    72  	if !v.AllErrors {
    73  		v.err = adt.CombineErrors(nil, v.err, b)
    74  		return
    75  	}
    76  	if !b.ChildError {
    77  		v.err = adt.CombineErrors(nil, v.err, b)
    78  	}
    79  }
    80  
    81  func (v *validator) validate(x *adt.Vertex) {
    82  	defer v.ctx.PopArcAndLabel(v.ctx.PushArcAndLabel(x))
    83  
    84  	y := x
    85  
    86  	// Dereference values, but only those that are not shared. This includes let
    87  	// values. This prevents us from processing structure-shared nodes more than
    88  	// once and prevents potential cycles.
    89  	x = x.DerefValue()
    90  	if y != x {
    91  		// Ensure that each structure shared node is processed at least once
    92  		// in a position that is not a definition.
    93  		if v.inDefinition > 0 {
    94  			return
    95  		}
    96  		if v.visited == nil {
    97  			v.visited = make(map[*adt.Vertex]bool)
    98  		}
    99  		if v.visited[x] {
   100  			return
   101  		}
   102  		v.visited[x] = true
   103  	}
   104  
   105  	if b := x.Bottom(); b != nil {
   106  		switch b.Code {
   107  		case adt.CycleError:
   108  			if v.checkFinal() || v.DisallowCycles {
   109  				v.add(b)
   110  			}
   111  
   112  		case adt.IncompleteError:
   113  			if v.checkFinal() {
   114  				v.add(b)
   115  			}
   116  
   117  		default:
   118  			v.add(b)
   119  		}
   120  		if !b.HasRecursive {
   121  			return
   122  		}
   123  
   124  	} else if v.checkConcrete() {
   125  		x = x.Default()
   126  		if !adt.IsConcrete(x) {
   127  			x := x.Value()
   128  			v.add(&adt.Bottom{
   129  				Code: adt.IncompleteError,
   130  				Err:  v.ctx.Newf("incomplete value %v", x),
   131  			})
   132  		}
   133  	}
   134  
   135  	for _, a := range x.Arcs {
   136  		if a.ArcType == adt.ArcRequired && v.Final && v.inDefinition == 0 {
   137  			v.ctx.PushArcAndLabel(a)
   138  			v.add(adt.NewRequiredNotPresentError(v.ctx, a))
   139  			v.ctx.PopArcAndLabel(a)
   140  			continue
   141  		}
   142  
   143  		if a.Label.IsLet() || !a.IsDefined(v.ctx) {
   144  			continue
   145  		}
   146  		if !v.AllErrors && v.err != nil {
   147  			break
   148  		}
   149  		if a.Label.IsRegular() {
   150  			v.validate(a)
   151  		} else {
   152  			v.inDefinition++
   153  			v.validate(a)
   154  			v.inDefinition--
   155  		}
   156  	}
   157  }