Skip to content

Commit

Permalink
Merge pull request #692 from hashicorp/support-incomplete-references-…
Browse files Browse the repository at this point in the history
…in-object-items

feat: return an `ExprSyntaxError` for invalid references that end in a dot
  • Loading branch information
ansgarm authored Aug 22, 2024
2 parents 360ae57 + 117baa8 commit ae53b93
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 3 deletions.
23 changes: 20 additions & 3 deletions hclsyntax/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,9 +811,16 @@ Traversal:
// will probably be misparsed until we hit something that
// allows us to re-sync.
//
// We will probably need to do something better here eventually
// in order to support autocomplete triggered by typing a
// period.
// Returning an ExprSyntaxError allows us to pass more information
// about the invalid expression to the caller, which can then
// use this for example for completions that happen after typing
// a dot in an editor.
ret = &ExprSyntaxError{
Placeholder: cty.DynamicVal,
ParseDiags: diags,
SrcRange: hcl.RangeBetween(from.Range(), dot.Range),
}

p.setRecovery()
}

Expand Down Expand Up @@ -1516,6 +1523,16 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
diags = append(diags, valueDiags...)

if p.recovery && valueDiags.HasErrors() {
// If the value is an ExprSyntaxError, we can add an item with it, even though we will recover afterwards
// This allows downstream consumers to still retrieve this first invalid item, even though following items
// won't be parsed. This is useful for supplying completions.
if exprSyntaxError, ok := value.(*ExprSyntaxError); ok {
items = append(items, ObjectConsItem{
KeyExpr: key,
ValueExpr: exprSyntaxError,
})
}

// If expression parsing failed then we are probably in a strange
// place in the token stream, so we'll bail out and try to reset
// to after our closing brace to allow parsing to continue.
Expand Down
81 changes: 81 additions & 0 deletions hclsyntax/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,87 @@ block "valid" {}
},
},
},
{
"a = { b = c. }",
1,
&Body{
Attributes: Attributes{
"a": {
Name: "a",
Expr: &ObjectConsExpr{
Items: []ObjectConsItem{
{
KeyExpr: &ObjectConsKeyExpr{
Wrapped: &ScopeTraversalExpr{
Traversal: hcl.Traversal{
hcl.TraverseRoot{
Name: "b",
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 7, Byte: 6},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
},
},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 7, Byte: 6},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
},
},
ValueExpr: &ExprSyntaxError{
Placeholder: cty.DynamicVal,
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 11, Byte: 10},
End: hcl.Pos{Line: 1, Column: 13, Byte: 12},
},
ParseDiags: hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "Invalid attribute name",
Detail: "An attribute name is required after a dot.",
Subject: &hcl.Range{
Start: hcl.Pos{Line: 1, Column: 14, Byte: 13},
End: hcl.Pos{Line: 1, Column: 15, Byte: 14},
},
},
},
},
},
},
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
End: hcl.Pos{Line: 1, Column: 15, Byte: 14},
},
OpenRange: 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: 15, Byte: 14},
},
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: 15, Byte: 14},
},
EndRange: hcl.Range{
Start: hcl.Pos{Line: 1, Column: 15, Byte: 14},
End: hcl.Pos{Line: 1, Column: 15, Byte: 14},
},
},
},
}

for _, test := range tests {
Expand Down

0 comments on commit ae53b93

Please sign in to comment.