aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hcl/json/structure.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/json/structure.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcl/json/structure.go616
1 files changed, 616 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcl/json/structure.go b/vendor/github.com/hashicorp/hcl2/hcl/json/structure.go
new file mode 100644
index 0000000..28dcf52
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcl/json/structure.go
@@ -0,0 +1,616 @@
1package json
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/hcl2/hcl"
7 "github.com/hashicorp/hcl2/hcl/hclsyntax"
8 "github.com/zclconf/go-cty/cty"
9 "github.com/zclconf/go-cty/cty/convert"
10)
11
12// body is the implementation of "Body" used for files processed with the JSON
13// parser.
14type body struct {
15 val node
16
17 // If non-nil, the keys of this map cause the corresponding attributes to
18 // be treated as non-existing. This is used when Body.PartialContent is
19 // called, to produce the "remaining content" Body.
20 hiddenAttrs map[string]struct{}
21}
22
23// expression is the implementation of "Expression" used for files processed
24// with the JSON parser.
25type expression struct {
26 src node
27}
28
29func (b *body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
30 content, newBody, diags := b.PartialContent(schema)
31
32 hiddenAttrs := newBody.(*body).hiddenAttrs
33
34 var nameSuggestions []string
35 for _, attrS := range schema.Attributes {
36 if _, ok := hiddenAttrs[attrS.Name]; !ok {
37 // Only suggest an attribute name if we didn't use it already.
38 nameSuggestions = append(nameSuggestions, attrS.Name)
39 }
40 }
41 for _, blockS := range schema.Blocks {
42 // Blocks can appear multiple times, so we'll suggest their type
43 // names regardless of whether they've already been used.
44 nameSuggestions = append(nameSuggestions, blockS.Type)
45 }
46
47 jsonAttrs, attrDiags := b.collectDeepAttrs(b.val, nil)
48 diags = append(diags, attrDiags...)
49
50 for _, attr := range jsonAttrs {
51 k := attr.Name
52 if k == "//" {
53 // Ignore "//" keys in objects representing bodies, to allow
54 // their use as comments.
55 continue
56 }
57
58 if _, ok := hiddenAttrs[k]; !ok {
59 suggestion := nameSuggestion(k, nameSuggestions)
60 if suggestion != "" {
61 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
62 }
63
64 diags = append(diags, &hcl.Diagnostic{
65 Severity: hcl.DiagError,
66 Summary: "Extraneous JSON object property",
67 Detail: fmt.Sprintf("No attribute or block type is named %q.%s", k, suggestion),
68 Subject: &attr.NameRange,
69 Context: attr.Range().Ptr(),
70 })
71 }
72 }
73
74 return content, diags
75}
76
77func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
78 var diags hcl.Diagnostics
79
80 jsonAttrs, attrDiags := b.collectDeepAttrs(b.val, nil)
81 diags = append(diags, attrDiags...)
82
83 usedNames := map[string]struct{}{}
84 if b.hiddenAttrs != nil {
85 for k := range b.hiddenAttrs {
86 usedNames[k] = struct{}{}
87 }
88 }
89
90 content := &hcl.BodyContent{
91 Attributes: map[string]*hcl.Attribute{},
92 Blocks: nil,
93
94 MissingItemRange: b.MissingItemRange(),
95 }
96
97 // Create some more convenient data structures for our work below.
98 attrSchemas := map[string]hcl.AttributeSchema{}
99 blockSchemas := map[string]hcl.BlockHeaderSchema{}
100 for _, attrS := range schema.Attributes {
101 attrSchemas[attrS.Name] = attrS
102 }
103 for _, blockS := range schema.Blocks {
104 blockSchemas[blockS.Type] = blockS
105 }
106
107 for _, jsonAttr := range jsonAttrs {
108 attrName := jsonAttr.Name
109 if _, used := b.hiddenAttrs[attrName]; used {
110 continue
111 }
112
113 if attrS, defined := attrSchemas[attrName]; defined {
114 if existing, exists := content.Attributes[attrName]; exists {
115 diags = append(diags, &hcl.Diagnostic{
116 Severity: hcl.DiagError,
117 Summary: "Duplicate attribute definition",
118 Detail: fmt.Sprintf("The attribute %q was already defined at %s.", attrName, existing.Range),
119 Subject: &jsonAttr.NameRange,
120 Context: jsonAttr.Range().Ptr(),
121 })
122 continue
123 }
124
125 content.Attributes[attrS.Name] = &hcl.Attribute{
126 Name: attrS.Name,
127 Expr: &expression{src: jsonAttr.Value},
128 Range: hcl.RangeBetween(jsonAttr.NameRange, jsonAttr.Value.Range()),
129 NameRange: jsonAttr.NameRange,
130 }
131 usedNames[attrName] = struct{}{}
132
133 } else if blockS, defined := blockSchemas[attrName]; defined {
134 bv := jsonAttr.Value
135 blockDiags := b.unpackBlock(bv, blockS.Type, &jsonAttr.NameRange, blockS.LabelNames, nil, nil, &content.Blocks)
136 diags = append(diags, blockDiags...)
137 usedNames[attrName] = struct{}{}
138 }
139
140 // We ignore anything that isn't defined because that's the
141 // PartialContent contract. The Content method will catch leftovers.
142 }
143
144 // Make sure we got all the required attributes.
145 for _, attrS := range schema.Attributes {
146 if !attrS.Required {
147 continue
148 }
149 if _, defined := content.Attributes[attrS.Name]; !defined {
150 diags = append(diags, &hcl.Diagnostic{
151 Severity: hcl.DiagError,
152 Summary: "Missing required attribute",
153 Detail: fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name),
154 Subject: b.MissingItemRange().Ptr(),
155 })
156 }
157 }
158
159 unusedBody := &body{
160 val: b.val,
161 hiddenAttrs: usedNames,
162 }
163
164 return content, unusedBody, diags
165}
166
167// JustAttributes for JSON bodies interprets all properties of the wrapped
168// JSON object as attributes and returns them.
169func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
170 var diags hcl.Diagnostics
171 attrs := make(map[string]*hcl.Attribute)
172
173 obj, ok := b.val.(*objectVal)
174 if !ok {
175 diags = append(diags, &hcl.Diagnostic{
176 Severity: hcl.DiagError,
177 Summary: "Incorrect JSON value type",
178 Detail: "A JSON object is required here, defining the attributes for this block.",
179 Subject: b.val.StartRange().Ptr(),
180 })
181 return attrs, diags
182 }
183
184 for _, jsonAttr := range obj.Attrs {
185 name := jsonAttr.Name
186 if name == "//" {
187 // Ignore "//" keys in objects representing bodies, to allow
188 // their use as comments.
189 continue
190 }
191
192 if _, hidden := b.hiddenAttrs[name]; hidden {
193 continue
194 }
195
196 if existing, exists := attrs[name]; exists {
197 diags = append(diags, &hcl.Diagnostic{
198 Severity: hcl.DiagError,
199 Summary: "Duplicate attribute definition",
200 Detail: fmt.Sprintf("The attribute %q was already defined at %s.", name, existing.Range),
201 Subject: &jsonAttr.NameRange,
202 })
203 continue
204 }
205
206 attrs[name] = &hcl.Attribute{
207 Name: name,
208 Expr: &expression{src: jsonAttr.Value},
209 Range: hcl.RangeBetween(jsonAttr.NameRange, jsonAttr.Value.Range()),
210 NameRange: jsonAttr.NameRange,
211 }
212 }
213
214 // No diagnostics possible here, since the parser already took care of
215 // finding duplicates and every JSON value can be a valid attribute value.
216 return attrs, diags
217}
218
219func (b *body) MissingItemRange() hcl.Range {
220 switch tv := b.val.(type) {
221 case *objectVal:
222 return tv.CloseRange
223 case *arrayVal:
224 return tv.OpenRange
225 default:
226 // Should not happen in correct operation, but might show up if the
227 // input is invalid and we are producing partial results.
228 return tv.StartRange()
229 }
230}
231
232func (b *body) unpackBlock(v node, typeName string, typeRange *hcl.Range, labelsLeft []string, labelsUsed []string, labelRanges []hcl.Range, blocks *hcl.Blocks) (diags hcl.Diagnostics) {
233 if len(labelsLeft) > 0 {
234 labelName := labelsLeft[0]
235 jsonAttrs, attrDiags := b.collectDeepAttrs(v, &labelName)
236 diags = append(diags, attrDiags...)
237
238 if len(jsonAttrs) == 0 {
239 diags = diags.Append(&hcl.Diagnostic{
240 Severity: hcl.DiagError,
241 Summary: "Missing block label",
242 Detail: fmt.Sprintf("At least one object property is required, whose name represents the %s block's %s.", typeName, labelName),
243 Subject: v.StartRange().Ptr(),
244 })
245 return
246 }
247 labelsUsed := append(labelsUsed, "")
248 labelRanges := append(labelRanges, hcl.Range{})
249 for _, p := range jsonAttrs {
250 pk := p.Name
251 labelsUsed[len(labelsUsed)-1] = pk
252 labelRanges[len(labelRanges)-1] = p.NameRange
253 diags = append(diags, b.unpackBlock(p.Value, typeName, typeRange, labelsLeft[1:], labelsUsed, labelRanges, blocks)...)
254 }
255 return
256 }
257
258 // By the time we get here, we've peeled off all the labels and we're ready
259 // to deal with the block's actual content.
260
261 // need to copy the label slices because their underlying arrays will
262 // continue to be mutated after we return.
263 labels := make([]string, len(labelsUsed))
264 copy(labels, labelsUsed)
265 labelR := make([]hcl.Range, len(labelRanges))
266 copy(labelR, labelRanges)
267
268 switch tv := v.(type) {
269 case *objectVal:
270 // Single instance of the block
271 *blocks = append(*blocks, &hcl.Block{
272 Type: typeName,
273 Labels: labels,
274 Body: &body{
275 val: tv,
276 },
277
278 DefRange: tv.OpenRange,
279 TypeRange: *typeRange,
280 LabelRanges: labelR,
281 })
282 case *arrayVal:
283 // Multiple instances of the block
284 for _, av := range tv.Values {
285 *blocks = append(*blocks, &hcl.Block{
286 Type: typeName,
287 Labels: labels,
288 Body: &body{
289 val: av, // might be mistyped; we'll find out when content is requested for this body
290 },
291
292 DefRange: tv.OpenRange,
293 TypeRange: *typeRange,
294 LabelRanges: labelR,
295 })
296 }
297 default:
298 diags = diags.Append(&hcl.Diagnostic{
299 Severity: hcl.DiagError,
300 Summary: "Incorrect JSON value type",
301 Detail: fmt.Sprintf("Either a JSON object or a JSON array is required, representing the contents of one or more %q blocks.", typeName),
302 Subject: v.StartRange().Ptr(),
303 })
304 }
305 return
306}
307
308// collectDeepAttrs takes either a single object or an array of objects and
309// flattens it into a list of object attributes, collecting attributes from
310// all of the objects in a given array.
311//
312// Ordering is preserved, so a list of objects that each have one property
313// will result in those properties being returned in the same order as the
314// objects appeared in the array.
315//
316// This is appropriate for use only for objects representing bodies or labels
317// within a block.
318//
319// The labelName argument, if non-null, is used to tailor returned error
320// messages to refer to block labels rather than attributes and child blocks.
321// It has no other effect.
322func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.Diagnostics) {
323 var diags hcl.Diagnostics
324 var attrs []*objectAttr
325
326 switch tv := v.(type) {
327
328 case *objectVal:
329 attrs = append(attrs, tv.Attrs...)
330
331 case *arrayVal:
332 for _, ev := range tv.Values {
333 switch tev := ev.(type) {
334 case *objectVal:
335 attrs = append(attrs, tev.Attrs...)
336 default:
337 if labelName != nil {
338 diags = append(diags, &hcl.Diagnostic{
339 Severity: hcl.DiagError,
340 Summary: "Incorrect JSON value type",
341 Detail: fmt.Sprintf("A JSON object is required here, to specify %s labels for this block.", *labelName),
342 Subject: ev.StartRange().Ptr(),
343 })
344 } else {
345 diags = append(diags, &hcl.Diagnostic{
346 Severity: hcl.DiagError,
347 Summary: "Incorrect JSON value type",
348 Detail: "A JSON object is required here, to define attributes and child blocks.",
349 Subject: ev.StartRange().Ptr(),
350 })
351 }
352 }
353 }
354
355 default:
356 if labelName != nil {
357 diags = append(diags, &hcl.Diagnostic{
358 Severity: hcl.DiagError,
359 Summary: "Incorrect JSON value type",
360 Detail: fmt.Sprintf("Either a JSON object or JSON array of objects is required here, to specify %s labels for this block.", *labelName),
361 Subject: v.StartRange().Ptr(),
362 })
363 } else {
364 diags = append(diags, &hcl.Diagnostic{
365 Severity: hcl.DiagError,
366 Summary: "Incorrect JSON value type",
367 Detail: "Either a JSON object or JSON array of objects is required here, to define attributes and child blocks.",
368 Subject: v.StartRange().Ptr(),
369 })
370 }
371 }
372
373 return attrs, diags
374}
375
376func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
377 switch v := e.src.(type) {
378 case *stringVal:
379 if ctx != nil {
380 // Parse string contents as a HCL native language expression.
381 // We only do this if we have a context, so passing a nil context
382 // is how the caller specifies that interpolations are not allowed
383 // and that the string should just be returned verbatim.
384 templateSrc := v.Value
385 expr, diags := hclsyntax.ParseTemplate(
386 []byte(templateSrc),
387 v.SrcRange.Filename,
388
389 // This won't produce _exactly_ the right result, since
390 // the hclsyntax parser can't "see" any escapes we removed
391 // while parsing JSON, but it's better than nothing.
392 hcl.Pos{
393 Line: v.SrcRange.Start.Line,
394
395 // skip over the opening quote mark
396 Byte: v.SrcRange.Start.Byte + 1,
397 Column: v.SrcRange.Start.Column + 1,
398 },
399 )
400 if diags.HasErrors() {
401 return cty.DynamicVal, diags
402 }
403 val, evalDiags := expr.Value(ctx)
404 diags = append(diags, evalDiags...)
405 return val, diags
406 }
407
408 return cty.StringVal(v.Value), nil
409 case *numberVal:
410 return cty.NumberVal(v.Value), nil
411 case *booleanVal:
412 return cty.BoolVal(v.Value), nil
413 case *arrayVal:
414 vals := []cty.Value{}
415 for _, jsonVal := range v.Values {
416 val, _ := (&expression{src: jsonVal}).Value(ctx)
417 vals = append(vals, val)
418 }
419 return cty.TupleVal(vals), nil
420 case *objectVal:
421 var diags hcl.Diagnostics
422 attrs := map[string]cty.Value{}
423 attrRanges := map[string]hcl.Range{}
424 known := true
425 for _, jsonAttr := range v.Attrs {
426 // In this one context we allow keys to contain interpolation
427 // experessions too, assuming we're evaluating in interpolation
428 // mode. This achieves parity with the native syntax where
429 // object expressions can have dynamic keys, while block contents
430 // may not.
431 name, nameDiags := (&expression{src: &stringVal{
432 Value: jsonAttr.Name,
433 SrcRange: jsonAttr.NameRange,
434 }}).Value(ctx)
435 val, valDiags := (&expression{src: jsonAttr.Value}).Value(ctx)
436 diags = append(diags, nameDiags...)
437 diags = append(diags, valDiags...)
438
439 var err error
440 name, err = convert.Convert(name, cty.String)
441 if err != nil {
442 diags = append(diags, &hcl.Diagnostic{
443 Severity: hcl.DiagError,
444 Summary: "Invalid object key expression",
445 Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err),
446 Subject: &jsonAttr.NameRange,
447 })
448 continue
449 }
450 if name.IsNull() {
451 diags = append(diags, &hcl.Diagnostic{
452 Severity: hcl.DiagError,
453 Summary: "Invalid object key expression",
454 Detail: "Cannot use null value as an object key.",
455 Subject: &jsonAttr.NameRange,
456 })
457 continue
458 }
459 if !name.IsKnown() {
460 // This is a bit of a weird case, since our usual rules require
461 // us to tolerate unknowns and just represent the result as
462 // best we can but if we don't know the key then we can't
463 // know the type of our object at all, and thus we must turn
464 // the whole thing into cty.DynamicVal. This is consistent with
465 // how this situation is handled in the native syntax.
466 // We'll keep iterating so we can collect other errors in
467 // subsequent attributes.
468 known = false
469 continue
470 }
471 nameStr := name.AsString()
472 if _, defined := attrs[nameStr]; defined {
473 diags = append(diags, &hcl.Diagnostic{
474 Severity: hcl.DiagError,
475 Summary: "Duplicate object attribute",
476 Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]),
477 Subject: &jsonAttr.NameRange,
478 })
479 continue
480 }
481 attrs[nameStr] = val
482 attrRanges[nameStr] = jsonAttr.NameRange
483 }
484 if !known {
485 // We encountered an unknown key somewhere along the way, so
486 // we can't know what our type will eventually be.
487 return cty.DynamicVal, diags
488 }
489 return cty.ObjectVal(attrs), diags
490 default:
491 // Default to DynamicVal so that ASTs containing invalid nodes can
492 // still be partially-evaluated.
493 return cty.DynamicVal, nil
494 }
495}
496
497func (e *expression) Variables() []hcl.Traversal {
498 var vars []hcl.Traversal
499
500 switch v := e.src.(type) {
501 case *stringVal:
502 templateSrc := v.Value
503 expr, diags := hclsyntax.ParseTemplate(
504 []byte(templateSrc),
505 v.SrcRange.Filename,
506
507 // This won't produce _exactly_ the right result, since
508 // the hclsyntax parser can't "see" any escapes we removed
509 // while parsing JSON, but it's better than nothing.
510 hcl.Pos{
511 Line: v.SrcRange.Start.Line,
512
513 // skip over the opening quote mark
514 Byte: v.SrcRange.Start.Byte + 1,
515 Column: v.SrcRange.Start.Column + 1,
516 },
517 )
518 if diags.HasErrors() {
519 return vars
520 }
521 return expr.Variables()
522
523 case *arrayVal:
524 for _, jsonVal := range v.Values {
525 vars = append(vars, (&expression{src: jsonVal}).Variables()...)
526 }
527 case *objectVal:
528 for _, jsonAttr := range v.Attrs {
529 vars = append(vars, (&expression{src: jsonAttr.Value}).Variables()...)
530 }
531 }
532
533 return vars
534}
535
536func (e *expression) Range() hcl.Range {
537 return e.src.Range()
538}
539
540func (e *expression) StartRange() hcl.Range {
541 return e.src.StartRange()
542}
543
544// Implementation for hcl.AbsTraversalForExpr.
545func (e *expression) AsTraversal() hcl.Traversal {
546 // In JSON-based syntax a traversal is given as a string containing
547 // traversal syntax as defined by hclsyntax.ParseTraversalAbs.
548
549 switch v := e.src.(type) {
550 case *stringVal:
551 traversal, diags := hclsyntax.ParseTraversalAbs([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start)
552 if diags.HasErrors() {
553 return nil
554 }
555 return traversal
556 default:
557 return nil
558 }
559}
560
561// Implementation for hcl.ExprCall.
562func (e *expression) ExprCall() *hcl.StaticCall {
563 // In JSON-based syntax a static call is given as a string containing
564 // an expression in the native syntax that also supports ExprCall.
565
566 switch v := e.src.(type) {
567 case *stringVal:
568 expr, diags := hclsyntax.ParseExpression([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start)
569 if diags.HasErrors() {
570 return nil
571 }
572
573 call, diags := hcl.ExprCall(expr)
574 if diags.HasErrors() {
575 return nil
576 }
577
578 return call
579 default:
580 return nil
581 }
582}
583
584// Implementation for hcl.ExprList.
585func (e *expression) ExprList() []hcl.Expression {
586 switch v := e.src.(type) {
587 case *arrayVal:
588 ret := make([]hcl.Expression, len(v.Values))
589 for i, node := range v.Values {
590 ret[i] = &expression{src: node}
591 }
592 return ret
593 default:
594 return nil
595 }
596}
597
598// Implementation for hcl.ExprMap.
599func (e *expression) ExprMap() []hcl.KeyValuePair {
600 switch v := e.src.(type) {
601 case *objectVal:
602 ret := make([]hcl.KeyValuePair, len(v.Attrs))
603 for i, jsonAttr := range v.Attrs {
604 ret[i] = hcl.KeyValuePair{
605 Key: &expression{src: &stringVal{
606 Value: jsonAttr.Name,
607 SrcRange: jsonAttr.NameRange,
608 }},
609 Value: &expression{src: jsonAttr.Value},
610 }
611 }
612 return ret
613 default:
614 return nil
615 }
616}