]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/hcl2/hcl/json/structure.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcl / json / structure.go
CommitLineData
15c0b25d
AP
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",
107c1cdb 67 Detail: fmt.Sprintf("No argument or block type is named %q.%s", k, suggestion),
15c0b25d
AP
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,
107c1cdb
ND
117 Summary: "Duplicate argument",
118 Detail: fmt.Sprintf("The argument %q was already set at %s.", attrName, existing.Range),
15c0b25d
AP
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,
107c1cdb
ND
152 Summary: "Missing required argument",
153 Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name),
15c0b25d
AP
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",
107c1cdb 178 Detail: "A JSON object is required here, setting the arguments for this block.",
15c0b25d
AP
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",
107c1cdb 200 Detail: fmt.Sprintf("The argument %q was already set at %s.", name, existing.Range),
15c0b25d
AP
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) {
107c1cdb
ND
269 case *nullVal:
270 // There is no block content, e.g the value is null.
271 return
15c0b25d
AP
272 case *objectVal:
273 // Single instance of the block
274 *blocks = append(*blocks, &hcl.Block{
275 Type: typeName,
276 Labels: labels,
277 Body: &body{
278 val: tv,
279 },
280
281 DefRange: tv.OpenRange,
282 TypeRange: *typeRange,
283 LabelRanges: labelR,
284 })
285 case *arrayVal:
286 // Multiple instances of the block
287 for _, av := range tv.Values {
288 *blocks = append(*blocks, &hcl.Block{
289 Type: typeName,
290 Labels: labels,
291 Body: &body{
292 val: av, // might be mistyped; we'll find out when content is requested for this body
293 },
294
295 DefRange: tv.OpenRange,
296 TypeRange: *typeRange,
297 LabelRanges: labelR,
298 })
299 }
300 default:
301 diags = diags.Append(&hcl.Diagnostic{
302 Severity: hcl.DiagError,
303 Summary: "Incorrect JSON value type",
304 Detail: fmt.Sprintf("Either a JSON object or a JSON array is required, representing the contents of one or more %q blocks.", typeName),
305 Subject: v.StartRange().Ptr(),
306 })
307 }
308 return
309}
310
311// collectDeepAttrs takes either a single object or an array of objects and
312// flattens it into a list of object attributes, collecting attributes from
313// all of the objects in a given array.
314//
315// Ordering is preserved, so a list of objects that each have one property
316// will result in those properties being returned in the same order as the
317// objects appeared in the array.
318//
319// This is appropriate for use only for objects representing bodies or labels
320// within a block.
321//
322// The labelName argument, if non-null, is used to tailor returned error
323// messages to refer to block labels rather than attributes and child blocks.
324// It has no other effect.
325func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.Diagnostics) {
326 var diags hcl.Diagnostics
327 var attrs []*objectAttr
328
329 switch tv := v.(type) {
107c1cdb
ND
330 case *nullVal:
331 // If a value is null, then we don't return any attributes or return an error.
15c0b25d
AP
332
333 case *objectVal:
334 attrs = append(attrs, tv.Attrs...)
335
336 case *arrayVal:
337 for _, ev := range tv.Values {
338 switch tev := ev.(type) {
339 case *objectVal:
340 attrs = append(attrs, tev.Attrs...)
341 default:
342 if labelName != nil {
343 diags = append(diags, &hcl.Diagnostic{
344 Severity: hcl.DiagError,
345 Summary: "Incorrect JSON value type",
346 Detail: fmt.Sprintf("A JSON object is required here, to specify %s labels for this block.", *labelName),
347 Subject: ev.StartRange().Ptr(),
348 })
349 } else {
350 diags = append(diags, &hcl.Diagnostic{
351 Severity: hcl.DiagError,
352 Summary: "Incorrect JSON value type",
107c1cdb 353 Detail: "A JSON object is required here, to define arguments and child blocks.",
15c0b25d
AP
354 Subject: ev.StartRange().Ptr(),
355 })
356 }
357 }
358 }
359
360 default:
361 if labelName != nil {
362 diags = append(diags, &hcl.Diagnostic{
363 Severity: hcl.DiagError,
364 Summary: "Incorrect JSON value type",
365 Detail: fmt.Sprintf("Either a JSON object or JSON array of objects is required here, to specify %s labels for this block.", *labelName),
366 Subject: v.StartRange().Ptr(),
367 })
368 } else {
369 diags = append(diags, &hcl.Diagnostic{
370 Severity: hcl.DiagError,
371 Summary: "Incorrect JSON value type",
107c1cdb 372 Detail: "Either a JSON object or JSON array of objects is required here, to define arguments and child blocks.",
15c0b25d
AP
373 Subject: v.StartRange().Ptr(),
374 })
375 }
376 }
377
378 return attrs, diags
379}
380
381func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
382 switch v := e.src.(type) {
383 case *stringVal:
384 if ctx != nil {
385 // Parse string contents as a HCL native language expression.
386 // We only do this if we have a context, so passing a nil context
387 // is how the caller specifies that interpolations are not allowed
388 // and that the string should just be returned verbatim.
389 templateSrc := v.Value
390 expr, diags := hclsyntax.ParseTemplate(
391 []byte(templateSrc),
392 v.SrcRange.Filename,
393
394 // This won't produce _exactly_ the right result, since
395 // the hclsyntax parser can't "see" any escapes we removed
396 // while parsing JSON, but it's better than nothing.
397 hcl.Pos{
398 Line: v.SrcRange.Start.Line,
399
400 // skip over the opening quote mark
401 Byte: v.SrcRange.Start.Byte + 1,
402 Column: v.SrcRange.Start.Column + 1,
403 },
404 )
405 if diags.HasErrors() {
406 return cty.DynamicVal, diags
407 }
408 val, evalDiags := expr.Value(ctx)
409 diags = append(diags, evalDiags...)
410 return val, diags
411 }
412
413 return cty.StringVal(v.Value), nil
414 case *numberVal:
415 return cty.NumberVal(v.Value), nil
416 case *booleanVal:
417 return cty.BoolVal(v.Value), nil
418 case *arrayVal:
419 vals := []cty.Value{}
420 for _, jsonVal := range v.Values {
421 val, _ := (&expression{src: jsonVal}).Value(ctx)
422 vals = append(vals, val)
423 }
424 return cty.TupleVal(vals), nil
425 case *objectVal:
426 var diags hcl.Diagnostics
427 attrs := map[string]cty.Value{}
428 attrRanges := map[string]hcl.Range{}
429 known := true
430 for _, jsonAttr := range v.Attrs {
431 // In this one context we allow keys to contain interpolation
107c1cdb 432 // expressions too, assuming we're evaluating in interpolation
15c0b25d
AP
433 // mode. This achieves parity with the native syntax where
434 // object expressions can have dynamic keys, while block contents
435 // may not.
436 name, nameDiags := (&expression{src: &stringVal{
437 Value: jsonAttr.Name,
438 SrcRange: jsonAttr.NameRange,
439 }}).Value(ctx)
107c1cdb
ND
440 valExpr := &expression{src: jsonAttr.Value}
441 val, valDiags := valExpr.Value(ctx)
15c0b25d
AP
442 diags = append(diags, nameDiags...)
443 diags = append(diags, valDiags...)
444
445 var err error
446 name, err = convert.Convert(name, cty.String)
447 if err != nil {
448 diags = append(diags, &hcl.Diagnostic{
107c1cdb
ND
449 Severity: hcl.DiagError,
450 Summary: "Invalid object key expression",
451 Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err),
452 Subject: &jsonAttr.NameRange,
453 Expression: valExpr,
454 EvalContext: ctx,
15c0b25d
AP
455 })
456 continue
457 }
458 if name.IsNull() {
459 diags = append(diags, &hcl.Diagnostic{
107c1cdb
ND
460 Severity: hcl.DiagError,
461 Summary: "Invalid object key expression",
462 Detail: "Cannot use null value as an object key.",
463 Subject: &jsonAttr.NameRange,
464 Expression: valExpr,
465 EvalContext: ctx,
15c0b25d
AP
466 })
467 continue
468 }
469 if !name.IsKnown() {
470 // This is a bit of a weird case, since our usual rules require
471 // us to tolerate unknowns and just represent the result as
472 // best we can but if we don't know the key then we can't
473 // know the type of our object at all, and thus we must turn
474 // the whole thing into cty.DynamicVal. This is consistent with
475 // how this situation is handled in the native syntax.
476 // We'll keep iterating so we can collect other errors in
477 // subsequent attributes.
478 known = false
479 continue
480 }
481 nameStr := name.AsString()
482 if _, defined := attrs[nameStr]; defined {
483 diags = append(diags, &hcl.Diagnostic{
107c1cdb
ND
484 Severity: hcl.DiagError,
485 Summary: "Duplicate object attribute",
486 Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]),
487 Subject: &jsonAttr.NameRange,
488 Expression: e,
489 EvalContext: ctx,
15c0b25d
AP
490 })
491 continue
492 }
493 attrs[nameStr] = val
494 attrRanges[nameStr] = jsonAttr.NameRange
495 }
496 if !known {
497 // We encountered an unknown key somewhere along the way, so
498 // we can't know what our type will eventually be.
499 return cty.DynamicVal, diags
500 }
501 return cty.ObjectVal(attrs), diags
107c1cdb
ND
502 case *nullVal:
503 return cty.NullVal(cty.DynamicPseudoType), nil
15c0b25d
AP
504 default:
505 // Default to DynamicVal so that ASTs containing invalid nodes can
506 // still be partially-evaluated.
507 return cty.DynamicVal, nil
508 }
509}
510
511func (e *expression) Variables() []hcl.Traversal {
512 var vars []hcl.Traversal
513
514 switch v := e.src.(type) {
515 case *stringVal:
516 templateSrc := v.Value
517 expr, diags := hclsyntax.ParseTemplate(
518 []byte(templateSrc),
519 v.SrcRange.Filename,
520
521 // This won't produce _exactly_ the right result, since
522 // the hclsyntax parser can't "see" any escapes we removed
523 // while parsing JSON, but it's better than nothing.
524 hcl.Pos{
525 Line: v.SrcRange.Start.Line,
526
527 // skip over the opening quote mark
528 Byte: v.SrcRange.Start.Byte + 1,
529 Column: v.SrcRange.Start.Column + 1,
530 },
531 )
532 if diags.HasErrors() {
533 return vars
534 }
535 return expr.Variables()
536
537 case *arrayVal:
538 for _, jsonVal := range v.Values {
539 vars = append(vars, (&expression{src: jsonVal}).Variables()...)
540 }
541 case *objectVal:
542 for _, jsonAttr := range v.Attrs {
107c1cdb
ND
543 keyExpr := &stringVal{ // we're going to treat key as an expression in this context
544 Value: jsonAttr.Name,
545 SrcRange: jsonAttr.NameRange,
546 }
547 vars = append(vars, (&expression{src: keyExpr}).Variables()...)
15c0b25d
AP
548 vars = append(vars, (&expression{src: jsonAttr.Value}).Variables()...)
549 }
550 }
551
552 return vars
553}
554
555func (e *expression) Range() hcl.Range {
556 return e.src.Range()
557}
558
559func (e *expression) StartRange() hcl.Range {
560 return e.src.StartRange()
561}
562
563// Implementation for hcl.AbsTraversalForExpr.
564func (e *expression) AsTraversal() hcl.Traversal {
565 // In JSON-based syntax a traversal is given as a string containing
566 // traversal syntax as defined by hclsyntax.ParseTraversalAbs.
567
568 switch v := e.src.(type) {
569 case *stringVal:
570 traversal, diags := hclsyntax.ParseTraversalAbs([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start)
571 if diags.HasErrors() {
572 return nil
573 }
574 return traversal
575 default:
576 return nil
577 }
578}
579
580// Implementation for hcl.ExprCall.
581func (e *expression) ExprCall() *hcl.StaticCall {
582 // In JSON-based syntax a static call is given as a string containing
583 // an expression in the native syntax that also supports ExprCall.
584
585 switch v := e.src.(type) {
586 case *stringVal:
587 expr, diags := hclsyntax.ParseExpression([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start)
588 if diags.HasErrors() {
589 return nil
590 }
591
592 call, diags := hcl.ExprCall(expr)
593 if diags.HasErrors() {
594 return nil
595 }
596
597 return call
598 default:
599 return nil
600 }
601}
602
603// Implementation for hcl.ExprList.
604func (e *expression) ExprList() []hcl.Expression {
605 switch v := e.src.(type) {
606 case *arrayVal:
607 ret := make([]hcl.Expression, len(v.Values))
608 for i, node := range v.Values {
609 ret[i] = &expression{src: node}
610 }
611 return ret
612 default:
613 return nil
614 }
615}
616
617// Implementation for hcl.ExprMap.
618func (e *expression) ExprMap() []hcl.KeyValuePair {
619 switch v := e.src.(type) {
620 case *objectVal:
621 ret := make([]hcl.KeyValuePair, len(v.Attrs))
622 for i, jsonAttr := range v.Attrs {
623 ret[i] = hcl.KeyValuePair{
624 Key: &expression{src: &stringVal{
625 Value: jsonAttr.Name,
626 SrcRange: jsonAttr.NameRange,
627 }},
628 Value: &expression{src: jsonAttr.Value},
629 }
630 }
631 return ret
632 default:
633 return nil
634 }
635}