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 }