From 7e26f2f3461267613602a534185654331d113c60 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 26 Feb 2019 15:41:59 -0800 Subject: [PATCH] hcl/hclsyntax: Parsing of for expressions while newline-sensitive The fact that object constructors are newline-sensitive while object for expressions are not requires some special consideration in the parser. We previously make a small fix here to delay turning on newline-sensitive scanning before peeking ahead for a "for" keyword, but that was sufficient only when the for expression was not already in a newline-sensitive context. Now we force newline-sensitive parsing off while we scan for the keyword, and also again once we begin parsing the for expression, ensuring that the for expression is always scanned properly regardless of what context it appears in. --- hcl/hclsyntax/expression_test.go | 14 ++++++++++++++ hcl/hclsyntax/parser.go | 10 +++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hcl/hclsyntax/expression_test.go b/hcl/hclsyntax/expression_test.go index 0a9f16d..30379f8 100644 --- a/hcl/hclsyntax/expression_test.go +++ b/hcl/hclsyntax/expression_test.go @@ -507,6 +507,20 @@ upper( }), 0, }, + { + // This one is different than the previous because the extra level of + // object constructor causes the inner for expression to begin parsing + // in newline-sensitive mode, which it must then properly disable in + // order to peek the "for" keyword. + "{\n a = {\n for k, v in {hello: \"world\"}:\nk => v\n }\n}", + nil, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "hello": cty.StringVal("world"), + }), + }), + 0, + }, { `{for k, v in {hello: "world"}: k => v if k == "hello"}`, nil, diff --git a/hcl/hclsyntax/parser.go b/hcl/hclsyntax/parser.go index 4a046f5..5513602 100644 --- a/hcl/hclsyntax/parser.go +++ b/hcl/hclsyntax/parser.go @@ -1242,7 +1242,13 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) { panic("parseObjectCons called without peeker pointing to open brace") } - if forKeyword.TokenMatches(p.Peek()) { + // We must temporarily stop looking at newlines here while we check for + // a "for" keyword, since for expressions are _not_ newline-sensitive, + // even though object constructors are. + p.PushIncludeNewlines(false) + isFor := forKeyword.TokenMatches(p.Peek()) + p.PopIncludeNewlines() + if isFor { return p.finishParsingForExpr(open) } @@ -1377,6 +1383,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) { } func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics) { + p.PushIncludeNewlines(false) + defer p.PopIncludeNewlines() introducer := p.Read() if !forKeyword.TokenMatches(introducer) { // Should never happen if callers are behaving