summaryrefslogtreecommitdiff
path: root/hclsyntax
diff options
context:
space:
mode:
authorAnsgar Mertens <ansgar@hashicorp.com>2024-08-22 10:14:20 +0200
committerGitHub <noreply@github.com>2024-08-22 10:14:20 +0200
commitae53b93a67f2cd4f2bedaaae6a64db6a1db8c6a1 (patch)
treef8a334690cb164297ce4e759531f14b44aa461e9 /hclsyntax
parent360ae579460fab69e9939599af85c8f255a59007 (diff)
parent117baa8bf53bc2050fe5e0f2ab85c4ef9341d20e (diff)
Merge pull request #692 from hashicorp/support-incomplete-references-in-object-items
feat: return an `ExprSyntaxError` for invalid references that end in a dot
Diffstat (limited to 'hclsyntax')
-rw-r--r--hclsyntax/parser.go23
-rw-r--r--hclsyntax/parser_test.go81
2 files changed, 101 insertions, 3 deletions
diff --git a/hclsyntax/parser.go b/hclsyntax/parser.go
index ce96ae3..fec7861 100644
--- a/hclsyntax/parser.go
+++ b/hclsyntax/parser.go
@@ -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()
}
@@ -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.
diff --git a/hclsyntax/parser_test.go b/hclsyntax/parser_test.go
index 10825f1..66f1308 100644
--- a/hclsyntax/parser_test.go
+++ b/hclsyntax/parser_test.go
@@ -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 {