diff --git a/decoder/expr_any_for.go b/decoder/expr_any_for.go index 46f1e4ef..0091cedf 100644 --- a/decoder/expr_any_for.go +++ b/decoder/expr_any_for.go @@ -7,6 +7,7 @@ import ( "context" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -155,6 +156,65 @@ func (a Any) semanticTokensForForExpr(ctx context.Context) ([]lang.SemanticToken return tokens, false } +func (a Any) refOriginsForForExpr(ctx context.Context, allowSelfRefs bool) (reference.Origins, bool) { + origins := make(reference.Origins, 0) + + // There is currently no way of decoding for expressions in JSON + // so we just collect them using the fallback logic assuming "any" + // constraint and focus on collecting expressions in HCL with more + // accurate constraints below. + + switch eType := a.expr.(type) { + case *hclsyntax.ForExpr: + if !isTypeIterable(a.cons.OfType) { + return nil, false + } + + // TODO: eType.KeyVarExpr.Range() to collect key as origin + // TODO: eType.ValVarExpr.Range() to collect value as origin + + if collExpr, ok := newExpression(a.pathCtx, eType.CollExpr, a.cons).(ReferenceOriginsExpression); ok { + origins = append(origins, collExpr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + + if eType.KeyExpr != nil { + typ, ok := iterableKeyType(a.cons.OfType) + if !ok { + return nil, false + } + cons := schema.AnyExpression{ + OfType: typ, + } + if keyExpr, ok := newExpression(a.pathCtx, eType.KeyExpr, cons).(ReferenceOriginsExpression); ok { + origins = append(origins, keyExpr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + } + + typ, ok := iterableValueType(a.cons.OfType) + if !ok { + return nil, false + } + cons := schema.AnyExpression{ + OfType: typ, + } + if valExpr, ok := newExpression(a.pathCtx, eType.ValExpr, cons).(ReferenceOriginsExpression); ok { + origins = append(origins, valExpr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + + if eType.CondExpr != nil { + cons := schema.AnyExpression{ + OfType: cty.Bool, + } + + if condExpr, ok := newExpression(a.pathCtx, eType.CondExpr, cons).(ReferenceOriginsExpression); ok { + origins = append(origins, condExpr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + } + } + + return origins, false +} + func isTypeIterable(typ cty.Type) bool { if typ == cty.DynamicPseudoType { return true diff --git a/decoder/expr_any_ref_origins.go b/decoder/expr_any_ref_origins.go index ff561c29..bb8bfbdd 100644 --- a/decoder/expr_any_ref_origins.go +++ b/decoder/expr_any_ref_origins.go @@ -117,7 +117,6 @@ func (a Any) ReferenceOrigins(ctx context.Context, allowSelfRefs bool) reference func (a Any) refOriginsForNonComplexExpr(ctx context.Context, allowSelfRefs bool) reference.Origins { // TODO: Support splat expression https://github.com/hashicorp/terraform-ls/issues/526 - // TODO: Support for-in-if expression https://github.com/hashicorp/terraform-ls/issues/527 // TODO: Support relative traversals https://github.com/hashicorp/terraform-ls/issues/532 if origins, ok := a.refOriginsForOperatorExpr(ctx, allowSelfRefs); ok { @@ -132,6 +131,10 @@ func (a Any) refOriginsForNonComplexExpr(ctx context.Context, allowSelfRefs bool return origins } + if origins, ok := a.refOriginsForForExpr(ctx, allowSelfRefs); ok { + return origins + } + // attempt to get accurate constraint for the origins // if we recognise the given expression funcExpr := functionExpr{