1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 package trim
49
50 import (
51 "io"
52 "os"
53
54 "cuelang.org/go/cue"
55 "cuelang.org/go/cue/ast"
56 "cuelang.org/go/cue/ast/astutil"
57 "cuelang.org/go/internal/core/adt"
58 "cuelang.org/go/internal/core/debug"
59 "cuelang.org/go/internal/core/subsume"
60 "cuelang.org/go/internal/core/walk"
61 "cuelang.org/go/internal/value"
62 )
63
64
65 type Config struct {
66 Trace bool
67 }
68
69
70
71
72
73 func Files(files []*ast.File, inst cue.InstanceOrValue, cfg *Config) error {
74 r, v := value.ToInternal(inst.Value())
75
76 t := &trimmer{
77 Config: *cfg,
78 ctx: adt.NewContext(r, v),
79 remove: map[ast.Node]bool{},
80 exclude: map[ast.Node]bool{},
81 debug: Debug,
82 w: os.Stderr,
83 }
84
85
86
87
88 visitor := &walk.Visitor{
89 Before: func(n adt.Node) bool {
90 switch x := n.(type) {
91 case *adt.StructLit:
92
93 for _, d := range x.Decls {
94 switch d.(type) {
95 case *adt.Comprehension:
96 t.markKeep(x)
97 }
98 }
99 }
100 return true
101 },
102 }
103 for _, c := range v.Conjuncts {
104 visitor.Elem(c.Elem())
105 }
106
107 d, _, _, pickedDefault := t.addDominators(nil, v, false)
108 t.findSubordinates(d, v, pickedDefault)
109
110
111 for _, f := range files {
112 astutil.Apply(f, func(c astutil.Cursor) bool {
113 if f, ok := c.Node().(*ast.Field); ok && t.remove[f.Value] && !t.exclude[f.Value] {
114 c.Delete()
115 }
116 return true
117 }, nil)
118 if err := astutil.Sanitize(f); err != nil {
119 return err
120 }
121 }
122
123 return nil
124 }
125
126 type trimmer struct {
127 Config
128
129 ctx *adt.OpContext
130 remove map[ast.Node]bool
131 exclude map[ast.Node]bool
132
133 debug bool
134 indent int
135 w io.Writer
136 }
137
138 var Debug bool = false
139
140 func (t *trimmer) markRemove(c adt.Conjunct) {
141 if src := c.Elem().Source(); src != nil {
142 t.remove[src] = true
143 if t.debug {
144 t.logf("removing %s", debug.NodeString(t.ctx, c.Elem(), nil))
145 }
146 }
147 }
148
149 func (t *trimmer) markKeep(x adt.Expr) {
150 if src := x.Source(); src != nil {
151 t.exclude[src] = true
152 if t.debug {
153 t.logf("keeping")
154 }
155 }
156 }
157
158 const dominatorNode = adt.ComprehensionSpan | adt.DefinitionSpan | adt.ConstraintSpan
159
160
161 func isDominator(c adt.Conjunct) (ok, mayRemove bool) {
162 if !c.CloseInfo.IsInOneOf(dominatorNode) {
163 return false, false
164 }
165 switch f := c.Field().(type) {
166 case *adt.Field:
167 return true, f.ArcType == adt.ArcMember
168 }
169 return true, true
170 }
171
172
173
174
175 func removable(c adt.Conjunct, v *adt.Vertex) bool {
176 return c.CloseInfo.FieldTypes&(adt.HasAdditional|adt.HasPattern) == 0
177 }
178
179
180
181 func (t *trimmer) allowRemove(v *adt.Vertex) bool {
182 for _, c := range v.Conjuncts {
183 _, allowRemove := isDominator(c)
184 loc := c.CloseInfo.Location() != c.Elem()
185 isSpan := c.CloseInfo.RootSpanType() != adt.ConstraintSpan
186 if allowRemove && (loc || isSpan) {
187 return true
188 }
189 }
190 return false
191 }
192
193
194
195
196
197 const (
198 no = 1 << iota
199 maybe
200 yes
201 )
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 func (t *trimmer) addDominators(d, v *adt.Vertex, hasDisjunction bool) (doms *adt.Vertex, ambiguous, hasSubs, strict bool) {
221 strict = hasDisjunction
222 doms = &adt.Vertex{
223 Parent: v.Parent,
224 Label: v.Label,
225 }
226 if d != nil && hasDisjunction {
227 doms.Conjuncts = append(doms.Conjuncts, d.Conjuncts...)
228 }
229
230 hasDoms := false
231 for _, c := range v.Conjuncts {
232 isDom, _ := isDominator(c)
233 switch {
234 case isDom:
235 doms.AddConjunct(c)
236 default:
237 if r, ok := c.Elem().(adt.Resolver); ok {
238 x, _ := t.ctx.Resolve(c, r)
239
240 if x != nil && x.Label.IsDef() {
241 for _, c := range x.Conjuncts {
242 doms.AddConjunct(c)
243 }
244 break
245 }
246 }
247 hasSubs = true
248 }
249 }
250 doms.Finalize(t.ctx)
251
252 switch x := doms.Value().(type) {
253 case *adt.Disjunction:
254 switch x.NumDefaults {
255 case 1:
256 strict = true
257 default:
258 ambiguous = true
259 }
260 }
261
262 if doms = doms.Default(); doms.IsErr() {
263 ambiguous = true
264 }
265
266 _ = hasDoms
267 return doms, hasSubs, ambiguous, strict || ambiguous
268 }
269
270 func (t *trimmer) findSubordinates(doms, v *adt.Vertex, hasDisjunction bool) (result int) {
271 defer un(t.trace(v))
272 defer func() {
273 if result == no {
274 for _, c := range v.Conjuncts {
275 t.markKeep(c.Expr())
276 }
277 }
278 }()
279
280 doms, hasSubs, ambiguous, pickedDefault := t.addDominators(doms, v, hasDisjunction)
281
282 if ambiguous {
283 return no
284 }
285
286
287
288
289
290 if len(v.Arcs) > 0 {
291 var match int
292 for _, a := range v.Arcs {
293 d := doms.Lookup(a.Label)
294 match |= t.findSubordinates(d, a, pickedDefault)
295 }
296
297
298
299
300
301 if match&yes == 0 || match&no != 0 {
302 return match
303 }
304 }
305
306 if !t.allowRemove(v) {
307 return no
308 }
309
310 switch v.BaseValue.(type) {
311 case *adt.StructMarker, *adt.ListMarker:
312
313
314
315
316 default:
317 if !hasSubs {
318 return maybe
319 }
320
321
322
323
324 v = v.Default()
325
326
327
328 if v.IsErr() {
329 return no
330 }
331
332
333
334
335 if err := subsume.Value(t.ctx, v, doms); err != nil {
336 return no
337 }
338 }
339
340 for _, c := range v.Conjuncts {
341 _, allowRemove := isDominator(c)
342 if !allowRemove && removable(c, v) {
343 t.markRemove(c)
344 }
345 }
346
347 return yes
348 }
349
View as plain text