diff options
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go')
-rw-r--r-- | vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/structure.go | 379 |
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 @@ | |||
1 | package hclsyntax | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/hcl2/hcl" | ||
8 | ) | ||
9 | |||
10 | // AsHCLBlock returns the block data expressed as a *hcl.Block. | ||
11 | func (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. | ||
29 | type 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 | ||
43 | var assertBodyImplBody hcl.Body = &Body{} | ||
44 | |||
45 | func (b *Body) walkChildNodes(w internalWalkFunc) { | ||
46 | b.Attributes = w(b.Attributes).(Attributes) | ||
47 | b.Blocks = w(b.Blocks).(Blocks) | ||
48 | } | ||
49 | |||
50 | func (b *Body) Range() hcl.Range { | ||
51 | return b.SrcRange | ||
52 | } | ||
53 | |||
54 | func (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 | |||
124 | func (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 | |||
246 | func (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 | |||
277 | func (b *Body) MissingItemRange() hcl.Range { | ||
278 | return b.EndRange | ||
279 | } | ||
280 | |||
281 | // Attributes is the collection of attribute definitions within a body. | ||
282 | type Attributes map[string]*Attribute | ||
283 | |||
284 | func (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. | ||
295 | func (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. | ||
310 | type Attribute struct { | ||
311 | Name string | ||
312 | Expr Expression | ||
313 | |||
314 | SrcRange hcl.Range | ||
315 | NameRange hcl.Range | ||
316 | EqualsRange hcl.Range | ||
317 | } | ||
318 | |||
319 | func (a *Attribute) walkChildNodes(w internalWalkFunc) { | ||
320 | a.Expr = w(a.Expr).(Expression) | ||
321 | } | ||
322 | |||
323 | func (a *Attribute) Range() hcl.Range { | ||
324 | return a.SrcRange | ||
325 | } | ||
326 | |||
327 | // AsHCLAttribute returns the block data expressed as a *hcl.Attribute. | ||
328 | func (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. | ||
339 | type Blocks []*Block | ||
340 | |||
341 | func (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. | ||
352 | func (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 | ||
362 | type 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 | |||
373 | func (b *Block) walkChildNodes(w internalWalkFunc) { | ||
374 | b.Body = w(b.Body).(*Body) | ||
375 | } | ||
376 | |||
377 | func (b *Block) Range() hcl.Range { | ||
378 | return hcl.RangeBetween(b.TypeRange, b.CloseBraceRange) | ||
379 | } | ||