From 9935743f9ec8d853f398583122871a6782e8e92c Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Tue, 20 Feb 2024 14:27:53 +0100 Subject: [PATCH] fix: handle nil expr for completion --- decoder/candidates.go | 5 ++++- decoder/candidates_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/decoder/candidates.go b/decoder/candidates.go index f9df622b..1e16eaa3 100644 --- a/decoder/candidates.go +++ b/decoder/candidates.go @@ -55,7 +55,10 @@ func (d *PathDecoder) completionAtPos(ctx context.Context, body *hclsyntax.Body, filename := body.Range().Filename for _, attr := range body.Attributes { - if d.isPosInsideAttrExpr(attr, pos) { + // TODO: handle nil Expr in all nested calls instead which allows us + // to recover incomplete calls to provider defined functions (which have no expression + // as they are deemed invalid by hcl while they are not completed yet) + if attr.Expr != nil && d.isPosInsideAttrExpr(attr, pos) { if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs { ctx = schema.WithActiveSelfRefs(ctx) } diff --git a/decoder/candidates_test.go b/decoder/candidates_test.go index 60ca4d37..d121f4de 100644 --- a/decoder/candidates_test.go +++ b/decoder/candidates_test.go @@ -1325,6 +1325,40 @@ resource "random_resource" "test" { } } +func TestDecoder_CompletionAtPos_nil_expr(t *testing.T) { + ctx := context.Background() + + // provider:: is not a traversal expression, so hcl will return a nil expression which needs to be + // handled gracefully + f, _ := hclsyntax.ParseConfig([]byte(`attr = provider::`), "test.tf", hcl.InitialPos) + + d := testPathDecoder(t, &PathContext{ + Schema: &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "attr": {Constraint: schema.AnyExpression{OfType: cty.DynamicPseudoType}}, + }, + }, + Files: map[string]*hcl.File{ + "test.tf": f, + }, + }) + + pos := hcl.Pos{Line: 1, Column: 16, Byte: 15} + + candidates, err := d.CompletionAtPos(ctx, "test.tf", pos) + if err != nil { + t.Fatal(err) + } + + expectedCandidates := lang.CompleteCandidates([]lang.Candidate{}) + + diff := cmp.Diff(expectedCandidates, candidates, ctydebug.CmpOptions) + if diff != "" { + t.Fatalf("unexpected schema for %s: %s", stringPos(pos), diff) + } + +} + func TestDecoder_CompletionAtPos_AnyAttribute(t *testing.T) { ctx := context.Background() providersSchema := &schema.BlockSchema{