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