aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go388
1 files changed, 294 insertions, 94 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go
index 002858f..253ad50 100644
--- a/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go
+++ b/vendor/github.com/hashicorp/hcl2/hcl/hclsyntax/parser.go
@@ -9,7 +9,6 @@ import (
9 "github.com/apparentlymart/go-textseg/textseg" 9 "github.com/apparentlymart/go-textseg/textseg"
10 "github.com/hashicorp/hcl2/hcl" 10 "github.com/hashicorp/hcl2/hcl"
11 "github.com/zclconf/go-cty/cty" 11 "github.com/zclconf/go-cty/cty"
12 "github.com/zclconf/go-cty/cty/convert"
13) 12)
14 13
15type parser struct { 14type parser struct {
@@ -55,7 +54,7 @@ Token:
55 Severity: hcl.DiagError, 54 Severity: hcl.DiagError,
56 Summary: "Attribute redefined", 55 Summary: "Attribute redefined",
57 Detail: fmt.Sprintf( 56 Detail: fmt.Sprintf(
58 "The attribute %q was already defined at %s. Each attribute may be defined only once.", 57 "The argument %q was already set at %s. Each argument may be set only once.",
59 titem.Name, existing.NameRange.String(), 58 titem.Name, existing.NameRange.String(),
60 ), 59 ),
61 Subject: &titem.NameRange, 60 Subject: &titem.NameRange,
@@ -80,15 +79,15 @@ Token:
80 if bad.Type == TokenOQuote { 79 if bad.Type == TokenOQuote {
81 diags = append(diags, &hcl.Diagnostic{ 80 diags = append(diags, &hcl.Diagnostic{
82 Severity: hcl.DiagError, 81 Severity: hcl.DiagError,
83 Summary: "Invalid attribute name", 82 Summary: "Invalid argument name",
84 Detail: "Attribute names must not be quoted.", 83 Detail: "Argument names must not be quoted.",
85 Subject: &bad.Range, 84 Subject: &bad.Range,
86 }) 85 })
87 } else { 86 } else {
88 diags = append(diags, &hcl.Diagnostic{ 87 diags = append(diags, &hcl.Diagnostic{
89 Severity: hcl.DiagError, 88 Severity: hcl.DiagError,
90 Summary: "Attribute or block definition required", 89 Summary: "Argument or block definition required",
91 Detail: "An attribute or block definition is required here.", 90 Detail: "An argument or block definition is required here.",
92 Subject: &bad.Range, 91 Subject: &bad.Range,
93 }) 92 })
94 } 93 }
@@ -120,8 +119,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
120 return nil, hcl.Diagnostics{ 119 return nil, hcl.Diagnostics{
121 { 120 {
122 Severity: hcl.DiagError, 121 Severity: hcl.DiagError,
123 Summary: "Attribute or block definition required", 122 Summary: "Argument or block definition required",
124 Detail: "An attribute or block definition is required here.", 123 Detail: "An argument or block definition is required here.",
125 Subject: &ident.Range, 124 Subject: &ident.Range,
126 }, 125 },
127 } 126 }
@@ -131,7 +130,7 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
131 130
132 switch next.Type { 131 switch next.Type {
133 case TokenEqual: 132 case TokenEqual:
134 return p.finishParsingBodyAttribute(ident) 133 return p.finishParsingBodyAttribute(ident, false)
135 case TokenOQuote, TokenOBrace, TokenIdent: 134 case TokenOQuote, TokenOBrace, TokenIdent:
136 return p.finishParsingBodyBlock(ident) 135 return p.finishParsingBodyBlock(ident)
137 default: 136 default:
@@ -139,8 +138,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
139 return nil, hcl.Diagnostics{ 138 return nil, hcl.Diagnostics{
140 { 139 {
141 Severity: hcl.DiagError, 140 Severity: hcl.DiagError,
142 Summary: "Attribute or block definition required", 141 Summary: "Argument or block definition required",
143 Detail: "An attribute or block definition is required here. To define an attribute, use the equals sign \"=\" to introduce the attribute value.", 142 Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
144 Subject: &ident.Range, 143 Subject: &ident.Range,
145 }, 144 },
146 } 145 }
@@ -149,7 +148,72 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
149 return nil, nil 148 return nil, nil
150} 149}
151 150
152func (p *parser) finishParsingBodyAttribute(ident Token) (Node, hcl.Diagnostics) { 151// parseSingleAttrBody is a weird variant of ParseBody that deals with the
152// body of a nested block containing only one attribute value all on a single
153// line, like foo { bar = baz } . It expects to find a single attribute item
154// immediately followed by the end token type with no intervening newlines.
155func (p *parser) parseSingleAttrBody(end TokenType) (*Body, hcl.Diagnostics) {
156 ident := p.Read()
157 if ident.Type != TokenIdent {
158 p.recoverAfterBodyItem()
159 return nil, hcl.Diagnostics{
160 {
161 Severity: hcl.DiagError,
162 Summary: "Argument or block definition required",
163 Detail: "An argument or block definition is required here.",
164 Subject: &ident.Range,
165 },
166 }
167 }
168
169 var attr *Attribute
170 var diags hcl.Diagnostics
171
172 next := p.Peek()
173
174 switch next.Type {
175 case TokenEqual:
176 node, attrDiags := p.finishParsingBodyAttribute(ident, true)
177 diags = append(diags, attrDiags...)
178 attr = node.(*Attribute)
179 case TokenOQuote, TokenOBrace, TokenIdent:
180 p.recoverAfterBodyItem()
181 return nil, hcl.Diagnostics{
182 {
183 Severity: hcl.DiagError,
184 Summary: "Argument definition required",
185 Detail: fmt.Sprintf("A single-line block definition can contain only a single argument. If you meant to define argument %q, use an equals sign to assign it a value. To define a nested block, place it on a line of its own within its parent block.", ident.Bytes),
186 Subject: hcl.RangeBetween(ident.Range, next.Range).Ptr(),
187 },
188 }
189 default:
190 p.recoverAfterBodyItem()
191 return nil, hcl.Diagnostics{
192 {
193 Severity: hcl.DiagError,
194 Summary: "Argument or block definition required",
195 Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
196 Subject: &ident.Range,
197 },
198 }
199 }
200
201 return &Body{
202 Attributes: Attributes{
203 string(ident.Bytes): attr,
204 },
205
206 SrcRange: attr.SrcRange,
207 EndRange: hcl.Range{
208 Filename: attr.SrcRange.Filename,
209 Start: attr.SrcRange.End,
210 End: attr.SrcRange.End,
211 },
212 }, diags
213
214}
215
216func (p *parser) finishParsingBodyAttribute(ident Token, singleLine bool) (Node, hcl.Diagnostics) {
153 eqTok := p.Read() // eat equals token 217 eqTok := p.Read() // eat equals token
154 if eqTok.Type != TokenEqual { 218 if eqTok.Type != TokenEqual {
155 // should never happen if caller behaves 219 // should never happen if caller behaves
@@ -166,22 +230,33 @@ func (p *parser) finishParsingBodyAttribute(ident Token) (Node, hcl.Diagnostics)
166 endRange = p.PrevRange() 230 endRange = p.PrevRange()
167 p.recoverAfterBodyItem() 231 p.recoverAfterBodyItem()
168 } else { 232 } else {
169 end := p.Peek() 233 endRange = p.PrevRange()
170 if end.Type != TokenNewline && end.Type != TokenEOF { 234 if !singleLine {
171 if !p.recovery { 235 end := p.Peek()
172 diags = append(diags, &hcl.Diagnostic{ 236 if end.Type != TokenNewline && end.Type != TokenEOF {
173 Severity: hcl.DiagError, 237 if !p.recovery {
174 Summary: "Missing newline after attribute definition", 238 summary := "Missing newline after argument"
175 Detail: "An attribute definition must end with a newline.", 239 detail := "An argument definition must end with a newline."
176 Subject: &end.Range, 240
177 Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(), 241 if end.Type == TokenComma {
178 }) 242 summary = "Unexpected comma after argument"
243 detail = "Argument definitions must be separated by newlines, not commas. " + detail
244 }
245
246 diags = append(diags, &hcl.Diagnostic{
247 Severity: hcl.DiagError,
248 Summary: summary,
249 Detail: detail,
250 Subject: &end.Range,
251 Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
252 })
253 }
254 endRange = p.PrevRange()
255 p.recoverAfterBodyItem()
256 } else {
257 endRange = p.PrevRange()
258 p.Read() // eat newline
179 } 259 }
180 endRange = p.PrevRange()
181 p.recoverAfterBodyItem()
182 } else {
183 endRange = p.PrevRange()
184 p.Read() // eat newline
185 } 260 }
186 } 261 }
187 262
@@ -218,19 +293,9 @@ Token:
218 diags = append(diags, labelDiags...) 293 diags = append(diags, labelDiags...)
219 labels = append(labels, label) 294 labels = append(labels, label)
220 labelRanges = append(labelRanges, labelRange) 295 labelRanges = append(labelRanges, labelRange)
221 if labelDiags.HasErrors() { 296 // parseQuoteStringLiteral recovers up to the closing quote
222 p.recoverAfterBodyItem() 297 // if it encounters problems, so we can continue looking for
223 return &Block{ 298 // more labels and eventually the block body even.
224 Type: blockType,
225 Labels: labels,
226 Body: nil,
227
228 TypeRange: ident.Range,
229 LabelRanges: labelRanges,
230 OpenBraceRange: ident.Range, // placeholder
231 CloseBraceRange: ident.Range, // placeholder
232 }, diags
233 }
234 299
235 case TokenIdent: 300 case TokenIdent:
236 tok = p.Read() // eat token 301 tok = p.Read() // eat token
@@ -244,7 +309,7 @@ Token:
244 diags = append(diags, &hcl.Diagnostic{ 309 diags = append(diags, &hcl.Diagnostic{
245 Severity: hcl.DiagError, 310 Severity: hcl.DiagError,
246 Summary: "Invalid block definition", 311 Summary: "Invalid block definition",
247 Detail: "The equals sign \"=\" indicates an attribute definition, and must not be used when defining a block.", 312 Detail: "The equals sign \"=\" indicates an argument definition, and must not be used when defining a block.",
248 Subject: &tok.Range, 313 Subject: &tok.Range,
249 Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(), 314 Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
250 }) 315 })
@@ -273,7 +338,10 @@ Token:
273 return &Block{ 338 return &Block{
274 Type: blockType, 339 Type: blockType,
275 Labels: labels, 340 Labels: labels,
276 Body: nil, 341 Body: &Body{
342 SrcRange: ident.Range,
343 EndRange: ident.Range,
344 },
277 345
278 TypeRange: ident.Range, 346 TypeRange: ident.Range,
279 LabelRanges: labelRanges, 347 LabelRanges: labelRanges,
@@ -285,7 +353,51 @@ Token:
285 353
286 // Once we fall out here, the peeker is pointed just after our opening 354 // Once we fall out here, the peeker is pointed just after our opening
287 // brace, so we can begin our nested body parsing. 355 // brace, so we can begin our nested body parsing.
288 body, bodyDiags := p.ParseBody(TokenCBrace) 356 var body *Body
357 var bodyDiags hcl.Diagnostics
358 switch p.Peek().Type {
359 case TokenNewline, TokenEOF, TokenCBrace:
360 body, bodyDiags = p.ParseBody(TokenCBrace)
361 default:
362 // Special one-line, single-attribute block parsing mode.
363 body, bodyDiags = p.parseSingleAttrBody(TokenCBrace)
364 switch p.Peek().Type {
365 case TokenCBrace:
366 p.Read() // the happy path - just consume the closing brace
367 case TokenComma:
368 // User seems to be trying to use the object-constructor
369 // comma-separated style, which isn't permitted for blocks.
370 diags = append(diags, &hcl.Diagnostic{
371 Severity: hcl.DiagError,
372 Summary: "Invalid single-argument block definition",
373 Detail: "Single-line block syntax can include only one argument definition. To define multiple arguments, use the multi-line block syntax with one argument definition per line.",
374 Subject: p.Peek().Range.Ptr(),
375 })
376 p.recover(TokenCBrace)
377 case TokenNewline:
378 // We don't allow weird mixtures of single and multi-line syntax.
379 diags = append(diags, &hcl.Diagnostic{
380 Severity: hcl.DiagError,
381 Summary: "Invalid single-argument block definition",
382 Detail: "An argument definition on the same line as its containing block creates a single-line block definition, which must also be closed on the same line. Place the block's closing brace immediately after the argument definition.",
383 Subject: p.Peek().Range.Ptr(),
384 })
385 p.recover(TokenCBrace)
386 default:
387 // Some other weird thing is going on. Since we can't guess a likely
388 // user intent for this one, we'll skip it if we're already in
389 // recovery mode.
390 if !p.recovery {
391 diags = append(diags, &hcl.Diagnostic{
392 Severity: hcl.DiagError,
393 Summary: "Invalid single-argument block definition",
394 Detail: "A single-line block definition must end with a closing brace immediately after its single argument definition.",
395 Subject: p.Peek().Range.Ptr(),
396 })
397 }
398 p.recover(TokenCBrace)
399 }
400 }
289 diags = append(diags, bodyDiags...) 401 diags = append(diags, bodyDiags...)
290 cBraceRange := p.PrevRange() 402 cBraceRange := p.PrevRange()
291 403
@@ -305,6 +417,17 @@ Token:
305 p.recoverAfterBodyItem() 417 p.recoverAfterBodyItem()
306 } 418 }
307 419
420 // We must never produce a nil body, since the caller may attempt to
421 // do analysis of a partial result when there's an error, so we'll
422 // insert a placeholder if we otherwise failed to produce a valid
423 // body due to one of the syntax error paths above.
424 if body == nil && diags.HasErrors() {
425 body = &Body{
426 SrcRange: hcl.RangeBetween(oBrace.Range, cBraceRange),
427 EndRange: cBraceRange,
428 }
429 }
430
308 return &Block{ 431 return &Block{
309 Type: blockType, 432 Type: blockType,
310 Labels: labels, 433 Labels: labels,
@@ -459,7 +582,14 @@ func (p *parser) parseBinaryOps(ops []map[TokenType]*Operation) (Expression, hcl
459 582
460func (p *parser) parseExpressionWithTraversals() (Expression, hcl.Diagnostics) { 583func (p *parser) parseExpressionWithTraversals() (Expression, hcl.Diagnostics) {
461 term, diags := p.parseExpressionTerm() 584 term, diags := p.parseExpressionTerm()
462 ret := term 585 ret, moreDiags := p.parseExpressionTraversals(term)
586 diags = append(diags, moreDiags...)
587 return ret, diags
588}
589
590func (p *parser) parseExpressionTraversals(from Expression) (Expression, hcl.Diagnostics) {
591 var diags hcl.Diagnostics
592 ret := from
463 593
464Traversal: 594Traversal:
465 for { 595 for {
@@ -657,44 +787,81 @@ Traversal:
657 // the key value is something constant. 787 // the key value is something constant.
658 788
659 open := p.Read() 789 open := p.Read()
660 // TODO: If we have a TokenStar inside our brackets, parse as 790 switch p.Peek().Type {
661 // a Splat expression: foo[*].baz[0]. 791 case TokenStar:
662 var close Token 792 // This is a full splat expression, like foo[*], which consumes
663 p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets 793 // the rest of the traversal steps after it using a recursive
664 keyExpr, keyDiags := p.ParseExpression() 794 // call to this function.
665 diags = append(diags, keyDiags...) 795 p.Read() // consume star
666 if p.recovery && keyDiags.HasErrors() { 796 close := p.Read()
667 close = p.recover(TokenCBrack)
668 } else {
669 close = p.Read()
670 if close.Type != TokenCBrack && !p.recovery { 797 if close.Type != TokenCBrack && !p.recovery {
671 diags = append(diags, &hcl.Diagnostic{ 798 diags = append(diags, &hcl.Diagnostic{
672 Severity: hcl.DiagError, 799 Severity: hcl.DiagError,
673 Summary: "Missing close bracket on index", 800 Summary: "Missing close bracket on splat index",
674 Detail: "The index operator must end with a closing bracket (\"]\").", 801 Detail: "The star for a full splat operator must be immediately followed by a closing bracket (\"]\").",
675 Subject: &close.Range, 802 Subject: &close.Range,
676 }) 803 })
677 close = p.recover(TokenCBrack) 804 close = p.recover(TokenCBrack)
678 } 805 }
679 } 806 // Splat expressions use a special "anonymous symbol" as a
680 p.PopIncludeNewlines() 807 // placeholder in an expression to be evaluated once for each
808 // item in the source expression.
809 itemExpr := &AnonSymbolExpr{
810 SrcRange: hcl.RangeBetween(open.Range, close.Range),
811 }
812 // Now we'll recursively call this same function to eat any
813 // remaining traversal steps against the anonymous symbol.
814 travExpr, nestedDiags := p.parseExpressionTraversals(itemExpr)
815 diags = append(diags, nestedDiags...)
681 816
682 if lit, isLit := keyExpr.(*LiteralValueExpr); isLit { 817 ret = &SplatExpr{
683 litKey, _ := lit.Value(nil) 818 Source: ret,
684 rng := hcl.RangeBetween(open.Range, close.Range) 819 Each: travExpr,
685 step := hcl.TraverseIndex{ 820 Item: itemExpr,
686 Key: litKey, 821
687 SrcRange: rng, 822 SrcRange: hcl.RangeBetween(open.Range, travExpr.Range()),
823 MarkerRange: hcl.RangeBetween(open.Range, close.Range),
688 } 824 }
689 ret = makeRelativeTraversal(ret, step, rng)
690 } else {
691 rng := hcl.RangeBetween(open.Range, close.Range)
692 ret = &IndexExpr{
693 Collection: ret,
694 Key: keyExpr,
695 825
696 SrcRange: rng, 826 default:
697 OpenRange: open.Range, 827
828 var close Token
829 p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets
830 keyExpr, keyDiags := p.ParseExpression()
831 diags = append(diags, keyDiags...)
832 if p.recovery && keyDiags.HasErrors() {
833 close = p.recover(TokenCBrack)
834 } else {
835 close = p.Read()
836 if close.Type != TokenCBrack && !p.recovery {
837 diags = append(diags, &hcl.Diagnostic{
838 Severity: hcl.DiagError,
839 Summary: "Missing close bracket on index",
840 Detail: "The index operator must end with a closing bracket (\"]\").",
841 Subject: &close.Range,
842 })
843 close = p.recover(TokenCBrack)
844 }
845 }
846 p.PopIncludeNewlines()
847
848 if lit, isLit := keyExpr.(*LiteralValueExpr); isLit {
849 litKey, _ := lit.Value(nil)
850 rng := hcl.RangeBetween(open.Range, close.Range)
851 step := hcl.TraverseIndex{
852 Key: litKey,
853 SrcRange: rng,
854 }
855 ret = makeRelativeTraversal(ret, step, rng)
856 } else {
857 rng := hcl.RangeBetween(open.Range, close.Range)
858 ret = &IndexExpr{
859 Collection: ret,
860 Key: keyExpr,
861
862 SrcRange: rng,
863 OpenRange: open.Range,
864 }
698 } 865 }
699 } 866 }
700 867
@@ -813,7 +980,7 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
813 case TokenOQuote, TokenOHeredoc: 980 case TokenOQuote, TokenOHeredoc:
814 open := p.Read() // eat opening marker 981 open := p.Read() // eat opening marker
815 closer := p.oppositeBracket(open.Type) 982 closer := p.oppositeBracket(open.Type)
816 exprs, passthru, _, diags := p.parseTemplateInner(closer) 983 exprs, passthru, _, diags := p.parseTemplateInner(closer, tokenOpensFlushHeredoc(open))
817 984
818 closeRange := p.PrevRange() 985 closeRange := p.PrevRange()
819 986
@@ -891,11 +1058,10 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
891} 1058}
892 1059
893func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) { 1060func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) {
894 // We'll lean on the cty converter to do the conversion, to ensure that 1061 // The cty.ParseNumberVal is always the same behavior as converting a
895 // the behavior is the same as what would happen if converting a 1062 // string to a number, ensuring we always interpret decimal numbers in
896 // non-literal string to a number. 1063 // the same way.
897 numStrVal := cty.StringVal(string(tok.Bytes)) 1064 numVal, err := cty.ParseNumberVal(string(tok.Bytes))
898 numVal, err := convert.Convert(numStrVal, cty.Number)
899 if err != nil { 1065 if err != nil {
900 ret := cty.UnknownVal(cty.Number) 1066 ret := cty.UnknownVal(cty.Number)
901 return ret, hcl.Diagnostics{ 1067 return ret, hcl.Diagnostics{
@@ -1087,13 +1253,19 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
1087 panic("parseObjectCons called without peeker pointing to open brace") 1253 panic("parseObjectCons called without peeker pointing to open brace")
1088 } 1254 }
1089 1255
1090 p.PushIncludeNewlines(true) 1256 // We must temporarily stop looking at newlines here while we check for
1091 defer p.PopIncludeNewlines() 1257 // a "for" keyword, since for expressions are _not_ newline-sensitive,
1092 1258 // even though object constructors are.
1093 if forKeyword.TokenMatches(p.Peek()) { 1259 p.PushIncludeNewlines(false)
1260 isFor := forKeyword.TokenMatches(p.Peek())
1261 p.PopIncludeNewlines()
1262 if isFor {
1094 return p.finishParsingForExpr(open) 1263 return p.finishParsingForExpr(open)
1095 } 1264 }
1096 1265
1266 p.PushIncludeNewlines(true)
1267 defer p.PopIncludeNewlines()
1268
1097 var close Token 1269 var close Token
1098 1270
1099 var diags hcl.Diagnostics 1271 var diags hcl.Diagnostics
@@ -1132,19 +1304,36 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
1132 next = p.Peek() 1304 next = p.Peek()
1133 if next.Type != TokenEqual && next.Type != TokenColon { 1305 if next.Type != TokenEqual && next.Type != TokenColon {
1134 if !p.recovery { 1306 if !p.recovery {
1135 if next.Type == TokenNewline || next.Type == TokenComma { 1307 switch next.Type {
1308 case TokenNewline, TokenComma:
1136 diags = append(diags, &hcl.Diagnostic{ 1309 diags = append(diags, &hcl.Diagnostic{
1137 Severity: hcl.DiagError, 1310 Severity: hcl.DiagError,
1138 Summary: "Missing item value", 1311 Summary: "Missing attribute value",
1139 Detail: "Expected an item value, introduced by an equals sign (\"=\").", 1312 Detail: "Expected an attribute value, introduced by an equals sign (\"=\").",
1140 Subject: &next.Range, 1313 Subject: &next.Range,
1141 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), 1314 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
1142 }) 1315 })
1143 } else { 1316 case TokenIdent:
1317 // Although this might just be a plain old missing equals
1318 // sign before a reference, one way to get here is to try
1319 // to write an attribute name containing a period followed
1320 // by a digit, which was valid in HCL1, like this:
1321 // foo1.2_bar = "baz"
1322 // We can't know exactly what the user intended here, but
1323 // we'll augment our message with an extra hint in this case
1324 // in case it is helpful.
1144 diags = append(diags, &hcl.Diagnostic{ 1325 diags = append(diags, &hcl.Diagnostic{
1145 Severity: hcl.DiagError, 1326 Severity: hcl.DiagError,
1146 Summary: "Missing key/value separator", 1327 Summary: "Missing key/value separator",
1147 Detail: "Expected an equals sign (\"=\") to mark the beginning of the item value.", 1328 Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value. If you intended to given an attribute name containing periods or spaces, write the name in quotes to create a string literal.",
1329 Subject: &next.Range,
1330 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
1331 })
1332 default:
1333 diags = append(diags, &hcl.Diagnostic{
1334 Severity: hcl.DiagError,
1335 Summary: "Missing key/value separator",
1336 Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value.",
1148 Subject: &next.Range, 1337 Subject: &next.Range,
1149 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), 1338 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
1150 }) 1339 })
@@ -1182,8 +1371,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
1182 if !p.recovery { 1371 if !p.recovery {
1183 diags = append(diags, &hcl.Diagnostic{ 1372 diags = append(diags, &hcl.Diagnostic{
1184 Severity: hcl.DiagError, 1373 Severity: hcl.DiagError,
1185 Summary: "Missing item separator", 1374 Summary: "Missing attribute separator",
1186 Detail: "Expected a newline or comma to mark the beginning of the next item.", 1375 Detail: "Expected a newline or comma to mark the beginning of the next attribute.",
1187 Subject: &next.Range, 1376 Subject: &next.Range,
1188 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), 1377 Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
1189 }) 1378 })
@@ -1205,6 +1394,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
1205} 1394}
1206 1395
1207func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics) { 1396func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics) {
1397 p.PushIncludeNewlines(false)
1398 defer p.PopIncludeNewlines()
1208 introducer := p.Read() 1399 introducer := p.Read()
1209 if !forKeyword.TokenMatches(introducer) { 1400 if !forKeyword.TokenMatches(introducer) {
1210 // Should never happen if callers are behaving 1401 // Should never happen if callers are behaving
@@ -1277,7 +1468,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
1277 diags = append(diags, &hcl.Diagnostic{ 1468 diags = append(diags, &hcl.Diagnostic{
1278 Severity: hcl.DiagError, 1469 Severity: hcl.DiagError,
1279 Summary: "Invalid 'for' expression", 1470 Summary: "Invalid 'for' expression",
1280 Detail: "For expression requires 'in' keyword after names.", 1471 Detail: "For expression requires the 'in' keyword after its name declarations.",
1281 Subject: p.Peek().Range.Ptr(), 1472 Subject: p.Peek().Range.Ptr(),
1282 Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), 1473 Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
1283 }) 1474 })
@@ -1305,7 +1496,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
1305 diags = append(diags, &hcl.Diagnostic{ 1496 diags = append(diags, &hcl.Diagnostic{
1306 Severity: hcl.DiagError, 1497 Severity: hcl.DiagError,
1307 Summary: "Invalid 'for' expression", 1498 Summary: "Invalid 'for' expression",
1308 Detail: "For expression requires colon after collection expression.", 1499 Detail: "For expression requires a colon after the collection expression.",
1309 Subject: p.Peek().Range.Ptr(), 1500 Subject: p.Peek().Range.Ptr(),
1310 Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), 1501 Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
1311 }) 1502 })
@@ -1459,7 +1650,7 @@ Token:
1459 case TokenTemplateControl, TokenTemplateInterp: 1650 case TokenTemplateControl, TokenTemplateInterp:
1460 which := "$" 1651 which := "$"
1461 if tok.Type == TokenTemplateControl { 1652 if tok.Type == TokenTemplateControl {
1462 which = "!" 1653 which = "%"
1463 } 1654 }
1464 1655
1465 diags = append(diags, &hcl.Diagnostic{ 1656 diags = append(diags, &hcl.Diagnostic{
@@ -1472,7 +1663,16 @@ Token:
1472 Subject: &tok.Range, 1663 Subject: &tok.Range,
1473 Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(), 1664 Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
1474 }) 1665 })
1475 p.recover(TokenTemplateSeqEnd) 1666
1667 // Now that we're returning an error callers won't attempt to use
1668 // the result for any real operations, but they might try to use
1669 // the partial AST for other analyses, so we'll leave a marker
1670 // to indicate that there was something invalid in the string to
1671 // help avoid misinterpretation of the partial result
1672 ret.WriteString(which)
1673 ret.WriteString("{ ... }")
1674
1675 p.recover(TokenTemplateSeqEnd) // we'll try to keep parsing after the sequence ends
1476 1676
1477 case TokenEOF: 1677 case TokenEOF:
1478 diags = append(diags, &hcl.Diagnostic{ 1678 diags = append(diags, &hcl.Diagnostic{
@@ -1493,7 +1693,7 @@ Token:
1493 Subject: &tok.Range, 1693 Subject: &tok.Range,
1494 Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(), 1694 Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
1495 }) 1695 })
1496 p.recover(TokenOQuote) 1696 p.recover(TokenCQuote)
1497 break Token 1697 break Token
1498 1698
1499 } 1699 }