aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go379
1 files changed, 379 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go
new file mode 100644
index 0000000..d69f65b
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go
@@ -0,0 +1,379 @@
1package hclsyntax
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/hashicorp/hcl2/hcl"
8)
9
10// AsHCLBlock returns the block data expressed as a *hcl.Block.
11func (b *Block) AsHCLBlock() *hcl.Block {
12 lastHeaderRange := b.TypeRange
13 if len(b.LabelRanges) > 0 {
14 lastHeaderRange = b.LabelRanges[len(b.LabelRanges)-1]
15 }
16
17 return &hcl.Block{
18 Type: b.Type,
19 Labels: b.Labels,
20 Body: b.Body,
21
22 DefRange: hcl.RangeBetween(b.TypeRange, lastHeaderRange),
23 TypeRange: b.TypeRange,
24 LabelRanges: b.LabelRanges,
25 }
26}
27
28// Body is the implementation of hcl.Body for the HCL native syntax.
29type Body struct {
30 Attributes Attributes
31 Blocks Blocks
32
33 // These are used with PartialContent to produce a "remaining items"
34 // body to return. They are nil on all bodies fresh out of the parser.
35 hiddenAttrs map[string]struct{}
36 hiddenBlocks map[string]struct{}
37
38 SrcRange hcl.Range
39 EndRange hcl.Range // Final token of the body, for reporting missing items
40}
41
42// Assert that *Body implements hcl.Body
43var assertBodyImplBody hcl.Body = &Body{}
44
45func (b *Body) walkChildNodes(w internalWalkFunc) {
46 b.Attributes = w(b.Attributes).(Attributes)
47 b.Blocks = w(b.Blocks).(Blocks)
48}
49
50func (b *Body) Range() hcl.Range {
51 return b.SrcRange
52}
53
54func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
55 content, remainHCL, diags := b.PartialContent(schema)
56
57 // No we'll see if anything actually remains, to produce errors about
58 // extraneous items.
59 remain := remainHCL.(*Body)
60
61 for name, attr := range b.Attributes {
62 if _, hidden := remain.hiddenAttrs[name]; !hidden {
63 var suggestions []string
64 for _, attrS := range schema.Attributes {
65 if _, defined := content.Attributes[attrS.Name]; defined {
66 continue
67 }
68 suggestions = append(suggestions, attrS.Name)
69 }
70 suggestion := nameSuggestion(name, suggestions)
71 if suggestion != "" {
72 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
73 } else {
74 // Is there a block of the same name?
75 for _, blockS := range schema.Blocks {
76 if blockS.Type == name {
77 suggestion = fmt.Sprintf(" Did you mean to define a block of type %q?", name)
78 break
79 }
80 }
81 }
82
83 diags = append(diags, &hcl.Diagnostic{
84 Severity: hcl.DiagError,
85 Summary: "Unsupported attribute",
86 Detail: fmt.Sprintf("An attribute named %q is not expected here.%s", name, suggestion),
87 Subject: &attr.NameRange,
88 })
89 }
90 }
91
92 for _, block := range b.Blocks {
93 blockTy := block.Type
94 if _, hidden := remain.hiddenBlocks[blockTy]; !hidden {
95 var suggestions []string
96 for _, blockS := range schema.Blocks {
97 suggestions = append(suggestions, blockS.Type)
98 }
99 suggestion := nameSuggestion(blockTy, suggestions)
100 if suggestion != "" {
101 suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
102 } else {
103 // Is there an attribute of the same name?
104 for _, attrS := range schema.Attributes {
105 if attrS.Name == blockTy {
106 suggestion = fmt.Sprintf(" Did you mean to define attribute %q?", blockTy)
107 break
108 }
109 }
110 }
111
112 diags = append(diags, &hcl.Diagnostic{
113 Severity: hcl.DiagError,
114 Summary: "Unsupported block type",
115 Detail: fmt.Sprintf("Blocks of type %q are not expected here.%s", blockTy, suggestion),
116 Subject: &block.TypeRange,
117 })
118 }
119 }
120
121 return content, diags
122}
123
124func (b *Body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
125 attrs := make(hcl.Attributes)
126 var blocks hcl.Blocks
127 var diags hcl.Diagnostics
128 hiddenAttrs := make(map[string]struct{})
129 hiddenBlocks := make(map[string]struct{})
130
131 if b.hiddenAttrs != nil {
132 for k, v := range b.hiddenAttrs {
133 hiddenAttrs[k] = v
134 }
135 }
136 if b.hiddenBlocks != nil {
137 for k, v := range b.hiddenBlocks {
138 hiddenBlocks[k] = v
139 }
140 }
141
142 for _, attrS := range schema.Attributes {
143 name := attrS.Name
144 attr, exists := b.Attributes[name]
145 _, hidden := hiddenAttrs[name]
146 if hidden || !exists {
147 if attrS.Required {
148 diags = append(diags, &hcl.Diagnostic{
149 Severity: hcl.DiagError,
150 Summary: "Missing required attribute",
151 Detail: fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name),
152 Subject: b.MissingItemRange().Ptr(),
153 })
154 }
155 continue
156 }
157
158 hiddenAttrs[name] = struct{}{}
159 attrs[name] = attr.AsHCLAttribute()
160 }
161
162 blocksWanted := make(map[string]hcl.BlockHeaderSchema)
163 for _, blockS := range schema.Blocks {
164 blocksWanted[blockS.Type] = blockS
165 }
166
167 for _, block := range b.Blocks {
168 if _, hidden := hiddenBlocks[block.Type]; hidden {
169 continue
170 }
171 blockS, wanted := blocksWanted[block.Type]
172 if !wanted {
173 continue
174 }
175
176 if len(block.Labels) > len(blockS.LabelNames) {
177 name := block.Type
178 if len(blockS.LabelNames) == 0 {
179 diags = append(diags, &hcl.Diagnostic{
180 Severity: hcl.DiagError,
181 Summary: fmt.Sprintf("Extraneous label for %s", name),
182 Detail: fmt.Sprintf(
183 "No labels are expected for %s blocks.", name,
184 ),
185 Subject: block.LabelRanges[0].Ptr(),
186 Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
187 })
188 } else {
189 diags = append(diags, &hcl.Diagnostic{
190 Severity: hcl.DiagError,
191 Summary: fmt.Sprintf("Extraneous label for %s", name),
192 Detail: fmt.Sprintf(
193 "Only %d labels (%s) are expected for %s blocks.",
194 len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "), name,
195 ),
196 Subject: block.LabelRanges[len(blockS.LabelNames)].Ptr(),
197 Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
198 })
199 }
200 continue
201 }
202
203 if len(block.Labels) < len(blockS.LabelNames) {
204 name := block.Type
205 diags = append(diags, &hcl.Diagnostic{
206 Severity: hcl.DiagError,
207 Summary: fmt.Sprintf("Missing %s for %s", blockS.LabelNames[len(block.Labels)], name),
208 Detail: fmt.Sprintf(
209 "All %s blocks must have %d labels (%s).",
210 name, len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "),
211 ),
212 Subject: &block.OpenBraceRange,
213 Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
214 })
215 continue
216 }
217
218 blocks = append(blocks, block.AsHCLBlock())
219 }
220
221 // We hide blocks only after we've processed all of them, since otherwise
222 // we can't process more than one of the same type.
223 for _, blockS := range schema.Blocks {
224 hiddenBlocks[blockS.Type] = struct{}{}
225 }
226
227 remain := &Body{
228 Attributes: b.Attributes,
229 Blocks: b.Blocks,
230
231 hiddenAttrs: hiddenAttrs,
232 hiddenBlocks: hiddenBlocks,
233
234 SrcRange: b.SrcRange,
235 EndRange: b.EndRange,
236 }
237
238 return &hcl.BodyContent{
239 Attributes: attrs,
240 Blocks: blocks,
241
242 MissingItemRange: b.MissingItemRange(),
243 }, remain, diags
244}
245
246func (b *Body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
247 attrs := make(hcl.Attributes)
248 var diags hcl.Diagnostics
249
250 if len(b.Blocks) > 0 {
251 example := b.Blocks[0]
252 diags = append(diags, &hcl.Diagnostic{
253 Severity: hcl.DiagError,
254 Summary: fmt.Sprintf("Unexpected %s block", example.Type),
255 Detail: "Blocks are not allowed here.",
256 Context: &example.TypeRange,
257 })
258 // we will continue processing anyway, and return the attributes
259 // we are able to find so that certain analyses can still be done
260 // in the face of errors.
261 }
262
263 if b.Attributes == nil {
264 return attrs, diags
265 }
266
267 for name, attr := range b.Attributes {
268 if _, hidden := b.hiddenAttrs[name]; hidden {
269 continue
270 }
271 attrs[name] = attr.AsHCLAttribute()
272 }
273
274 return attrs, diags
275}
276
277func (b *Body) MissingItemRange() hcl.Range {
278 return b.EndRange
279}
280
281// Attributes is the collection of attribute definitions within a body.
282type Attributes map[string]*Attribute
283
284func (a Attributes) walkChildNodes(w internalWalkFunc) {
285 for k, attr := range a {
286 a[k] = w(attr).(*Attribute)
287 }
288}
289
290// Range returns the range of some arbitrary point within the set of
291// attributes, or an invalid range if there are no attributes.
292//
293// This is provided only to complete the Node interface, but has no practical
294// use.
295func (a Attributes) Range() hcl.Range {
296 // An attributes doesn't really have a useful range to report, since
297 // it's just a grouping construct. So we'll arbitrarily take the
298 // range of one of the attributes, or produce an invalid range if we have
299 // none. In practice, there's little reason to ask for the range of
300 // an Attributes.
301 for _, attr := range a {
302 return attr.Range()
303 }
304 return hcl.Range{
305 Filename: "<unknown>",
306 }
307}
308
309// Attribute represents a single attribute definition within a body.
310type Attribute struct {
311 Name string
312 Expr Expression
313
314 SrcRange hcl.Range
315 NameRange hcl.Range
316 EqualsRange hcl.Range
317}
318
319func (a *Attribute) walkChildNodes(w internalWalkFunc) {
320 a.Expr = w(a.Expr).(Expression)
321}
322
323func (a *Attribute) Range() hcl.Range {
324 return a.SrcRange
325}
326
327// AsHCLAttribute returns the block data expressed as a *hcl.Attribute.
328func (a *Attribute) AsHCLAttribute() *hcl.Attribute {
329 return &hcl.Attribute{
330 Name: a.Name,
331 Expr: a.Expr,
332
333 Range: a.SrcRange,
334 NameRange: a.NameRange,
335 }
336}
337
338// Blocks is the list of nested blocks within a body.
339type Blocks []*Block
340
341func (bs Blocks) walkChildNodes(w internalWalkFunc) {
342 for i, block := range bs {
343 bs[i] = w(block).(*Block)
344 }
345}
346
347// Range returns the range of some arbitrary point within the list of
348// blocks, or an invalid range if there are no blocks.
349//
350// This is provided only to complete the Node interface, but has no practical
351// use.
352func (bs Blocks) Range() hcl.Range {
353 if len(bs) > 0 {
354 return bs[0].Range()
355 }
356 return hcl.Range{
357 Filename: "<unknown>",
358 }
359}
360
361// Block represents a nested block structure
362type Block struct {
363 Type string
364 Labels []string
365 Body *Body
366
367 TypeRange hcl.Range
368 LabelRanges []hcl.Range
369 OpenBraceRange hcl.Range
370 CloseBraceRange hcl.Range
371}
372
373func (b *Block) walkChildNodes(w internalWalkFunc) {
374 b.Body = w(b.Body).(*Body)
375}
376
377func (b *Block) Range() hcl.Range {
378 return hcl.RangeBetween(b.TypeRange, b.CloseBraceRange)
379}