hclsyntax: allow missing newline at EOF

Due to some earlier limitations of the parser we required each attribute
and block to end with a newline, even if it appeared at the end of a
file. In effect, this required all files to end with a newline character.

This is no longer required and so we'll tolerate that missing newline for
pragmatic reasons.
This commit is contained in:
Martin Atkins 2018-03-03 07:46:04 -08:00
parent 998a3053e2
commit 386ab3257c
2 changed files with 105 additions and 36 deletions

View File

@ -167,25 +167,15 @@ func (p *parser) finishParsingBodyAttribute(ident Token) (Node, hcl.Diagnostics)
p.recoverAfterBodyItem()
} else {
end := p.Peek()
if end.Type != TokenNewline {
if end.Type != TokenNewline && end.Type != TokenEOF {
if !p.recovery {
if end.Type == TokenEOF {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after attribute definition",
Detail: "A newline is required after an attribute definition at the end of a file.",
Subject: &end.Range,
Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
})
} else {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after attribute definition",
Detail: "An attribute definition must end with a newline.",
Subject: &end.Range,
Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
})
}
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after attribute definition",
Detail: "An attribute definition must end with a newline.",
Subject: &end.Range,
Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
})
}
endRange = p.PrevRange()
p.recoverAfterBodyItem()
@ -294,27 +284,17 @@ Token:
cBraceRange := p.PrevRange()
eol := p.Peek()
if eol.Type == TokenNewline {
if eol.Type == TokenNewline || eol.Type == TokenEOF {
p.Read() // eat newline
} else {
if !p.recovery {
if eol.Type == TokenEOF {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after block definition",
Detail: "A newline is required after a block definition at the end of a file.",
Subject: &eol.Range,
Context: hcl.RangeBetween(ident.Range, eol.Range).Ptr(),
})
} else {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after block definition",
Detail: "A block definition must end with a newline.",
Subject: &eol.Range,
Context: hcl.RangeBetween(ident.Range, eol.Range).Ptr(),
})
}
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after block definition",
Detail: "A block definition must end with a newline.",
Subject: &eol.Range,
Context: hcl.RangeBetween(ident.Range, eol.Range).Ptr(),
})
}
p.recoverAfterBodyItem()
}

View File

@ -79,6 +79,54 @@ func TestParseConfig(t *testing.T) {
},
},
},
{
"block {}",
0,
&Body{
Attributes: Attributes{},
Blocks: Blocks{
&Block{
Type: "block",
Labels: nil,
Body: &Body{
Attributes: Attributes{},
Blocks: Blocks{},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 7, Byte: 6},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
EndRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
},
TypeRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
LabelRanges: nil,
OpenBraceRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 7, Byte: 6},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
CloseBraceRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 8, Byte: 7},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
},
},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
EndRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
},
},
{
"block {}block {}\n",
1, // missing newline after block definition
@ -405,6 +453,47 @@ block "valid" {}
},
},
},
{
"a = 1",
0,
&Body{
Attributes: Attributes{
"a": {
Name: "a",
Expr: &LiteralValueExpr{
Val: cty.NumberIntVal(1),
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
NameRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 2, Byte: 1},
},
EqualsRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 3, Byte: 2},
End: hcl.Pos{Line: 1, Column: 4, Byte: 3},
},
},
},
Blocks: Blocks{},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
EndRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 6, Byte: 5},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
},
},
{
"a = \"hello ${true}\"\n",
0,