diff --git a/decoder/expr_any_completion.go b/decoder/expr_any_completion.go index c297e46e..0f6b36eb 100644 --- a/decoder/expr_any_completion.go +++ b/decoder/expr_any_completion.go @@ -279,33 +279,3 @@ func (a Any) completeTemplateExprAtPos(ctx context.Context, pos hcl.Pos) ([]lang return candidates, true } - -func (a Any) completeConditionalExprAtPos(ctx context.Context, pos hcl.Pos) ([]lang.Candidate, bool) { - candidates := make([]lang.Candidate, 0) - - switch eType := a.expr.(type) { - case *hclsyntax.ConditionalExpr: - if eType.Condition.Range().ContainsPos(pos) || eType.Condition.Range().End.Byte == pos.Byte { - cons := schema.AnyExpression{ - OfType: cty.Bool, - } - return newExpression(a.pathCtx, eType.Condition, cons).CompletionAtPos(ctx, pos), true - } - if eType.TrueResult.Range().ContainsPos(pos) || eType.TrueResult.Range().End.Byte == pos.Byte { - cons := schema.AnyExpression{ - OfType: cty.DynamicPseudoType, - } - return newExpression(a.pathCtx, eType.TrueResult, cons).CompletionAtPos(ctx, pos), true - } - if eType.FalseResult.Range().ContainsPos(pos) || eType.FalseResult.Range().End.Byte == pos.Byte { - cons := schema.AnyExpression{ - OfType: cty.DynamicPseudoType, - } - return newExpression(a.pathCtx, eType.FalseResult, cons).CompletionAtPos(ctx, pos), true - } - - return candidates, false - } - - return candidates, true -} diff --git a/decoder/expr_any_hover.go b/decoder/expr_any_hover.go index dd8272cf..3f9f5fe0 100644 --- a/decoder/expr_any_hover.go +++ b/decoder/expr_any_hover.go @@ -229,29 +229,3 @@ func (a Any) hoverTemplateExprAtPos(ctx context.Context, pos hcl.Pos) (*lang.Hov return nil, false } - -func (a Any) hoverConditionalExprAtPos(ctx context.Context, pos hcl.Pos) (*lang.HoverData, bool) { - switch eType := a.expr.(type) { - case *hclsyntax.ConditionalExpr: - if eType.Condition.Range().ContainsPos(pos) { - cons := schema.AnyExpression{ - OfType: cty.Bool, - } - return newExpression(a.pathCtx, eType.Condition, cons).HoverAtPos(ctx, pos), true - } - if eType.TrueResult.Range().ContainsPos(pos) { - cons := schema.AnyExpression{ - OfType: cty.DynamicPseudoType, - } - return newExpression(a.pathCtx, eType.TrueResult, cons).HoverAtPos(ctx, pos), true - } - if eType.FalseResult.Range().ContainsPos(pos) { - cons := schema.AnyExpression{ - OfType: cty.DynamicPseudoType, - } - return newExpression(a.pathCtx, eType.FalseResult, cons).HoverAtPos(ctx, pos), true - } - } - - return nil, false -} diff --git a/decoder/expr_any_ref_origins.go b/decoder/expr_any_ref_origins.go index ad360a9e..af480749 100644 --- a/decoder/expr_any_ref_origins.go +++ b/decoder/expr_any_ref_origins.go @@ -283,42 +283,3 @@ func (a Any) refOriginsForTemplateExpr(ctx context.Context, allowSelfRefs bool) return origins, false } - -func (a Any) refOriginsForConditionalExpr(ctx context.Context, allowSelfRefs bool) (reference.Origins, bool) { - origins := make(reference.Origins, 0) - - // There is currently no way of decoding conditional 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.ConditionalExpr: - condExpr := newExpression(a.pathCtx, eType.Condition, schema.AnyExpression{ - OfType: cty.Bool, - }) - if expr, ok := condExpr.(ReferenceOriginsExpression); ok { - origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) - } - - trueExpr := newExpression(a.pathCtx, eType.TrueResult, schema.AnyExpression{ - OfType: a.cons.OfType, - SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, - }) - if expr, ok := trueExpr.(ReferenceOriginsExpression); ok { - origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) - } - - falseExpr := newExpression(a.pathCtx, eType.FalseResult, schema.AnyExpression{ - OfType: a.cons.OfType, - SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, - }) - if expr, ok := falseExpr.(ReferenceOriginsExpression); ok { - origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) - } - - return origins, true - } - - return origins, false -} diff --git a/decoder/expr_any_semtok.go b/decoder/expr_any_semtok.go index 5d230895..f913fd90 100644 --- a/decoder/expr_any_semtok.go +++ b/decoder/expr_any_semtok.go @@ -227,26 +227,3 @@ func (a Any) semanticTokensForTemplateExpr(ctx context.Context) ([]lang.Semantic return tokens, false } - -func (a Any) semanticTokensForConditionalExpr(ctx context.Context) ([]lang.SemanticToken, bool) { - tokens := make([]lang.SemanticToken, 0) - - switch eType := a.expr.(type) { - case *hclsyntax.ConditionalExpr: - tokens = append(tokens, newExpression(a.pathCtx, eType.Condition, schema.AnyExpression{ - OfType: cty.Bool, - }).SemanticTokens(ctx)...) - tokens = append(tokens, newExpression(a.pathCtx, eType.TrueResult, schema.AnyExpression{ - OfType: a.cons.OfType, - SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, - }).SemanticTokens(ctx)...) - tokens = append(tokens, newExpression(a.pathCtx, eType.FalseResult, schema.AnyExpression{ - OfType: a.cons.OfType, - SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, - }).SemanticTokens(ctx)...) - - return tokens, true - } - - return tokens, false -} diff --git a/decoder/expr_conditional.go b/decoder/expr_conditional.go new file mode 100644 index 00000000..c8aa5420 --- /dev/null +++ b/decoder/expr_conditional.go @@ -0,0 +1,133 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package decoder + +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" + "github.com/zclconf/go-cty/cty" +) + +func (a Any) completeConditionalExprAtPos(ctx context.Context, pos hcl.Pos) ([]lang.Candidate, bool) { + candidates := make([]lang.Candidate, 0) + + switch eType := a.expr.(type) { + case *hclsyntax.ConditionalExpr: + if eType.Condition.Range().ContainsPos(pos) || eType.Condition.Range().End.Byte == pos.Byte { + cons := schema.AnyExpression{ + OfType: cty.Bool, + } + return newExpression(a.pathCtx, eType.Condition, cons).CompletionAtPos(ctx, pos), true + } + if eType.TrueResult.Range().ContainsPos(pos) || eType.TrueResult.Range().End.Byte == pos.Byte { + cons := schema.AnyExpression{ + OfType: cty.DynamicPseudoType, + } + return newExpression(a.pathCtx, eType.TrueResult, cons).CompletionAtPos(ctx, pos), true + } + if eType.FalseResult.Range().ContainsPos(pos) || eType.FalseResult.Range().End.Byte == pos.Byte { + cons := schema.AnyExpression{ + OfType: cty.DynamicPseudoType, + } + return newExpression(a.pathCtx, eType.FalseResult, cons).CompletionAtPos(ctx, pos), true + } + + return candidates, false + } + + return candidates, true +} + +func (a Any) hoverConditionalExprAtPos(ctx context.Context, pos hcl.Pos) (*lang.HoverData, bool) { + switch eType := a.expr.(type) { + case *hclsyntax.ConditionalExpr: + if eType.Condition.Range().ContainsPos(pos) { + cons := schema.AnyExpression{ + OfType: cty.Bool, + } + return newExpression(a.pathCtx, eType.Condition, cons).HoverAtPos(ctx, pos), true + } + if eType.TrueResult.Range().ContainsPos(pos) { + cons := schema.AnyExpression{ + OfType: cty.DynamicPseudoType, + } + return newExpression(a.pathCtx, eType.TrueResult, cons).HoverAtPos(ctx, pos), true + } + if eType.FalseResult.Range().ContainsPos(pos) { + cons := schema.AnyExpression{ + OfType: cty.DynamicPseudoType, + } + return newExpression(a.pathCtx, eType.FalseResult, cons).HoverAtPos(ctx, pos), true + } + } + + return nil, false +} + +func (a Any) refOriginsForConditionalExpr(ctx context.Context, allowSelfRefs bool) (reference.Origins, bool) { + origins := make(reference.Origins, 0) + + // There is currently no way of decoding conditional 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.ConditionalExpr: + condExpr := newExpression(a.pathCtx, eType.Condition, schema.AnyExpression{ + OfType: cty.Bool, + }) + if expr, ok := condExpr.(ReferenceOriginsExpression); ok { + origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + + trueExpr := newExpression(a.pathCtx, eType.TrueResult, schema.AnyExpression{ + OfType: a.cons.OfType, + SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, + }) + if expr, ok := trueExpr.(ReferenceOriginsExpression); ok { + origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + + falseExpr := newExpression(a.pathCtx, eType.FalseResult, schema.AnyExpression{ + OfType: a.cons.OfType, + SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, + }) + if expr, ok := falseExpr.(ReferenceOriginsExpression); ok { + origins = append(origins, expr.ReferenceOrigins(ctx, allowSelfRefs)...) + } + + return origins, true + } + + return origins, false +} + +func (a Any) semanticTokensForConditionalExpr(ctx context.Context) ([]lang.SemanticToken, bool) { + tokens := make([]lang.SemanticToken, 0) + + switch eType := a.expr.(type) { + case *hclsyntax.ConditionalExpr: + tokens = append(tokens, newExpression(a.pathCtx, eType.Condition, schema.AnyExpression{ + OfType: cty.Bool, + }).SemanticTokens(ctx)...) + tokens = append(tokens, newExpression(a.pathCtx, eType.TrueResult, schema.AnyExpression{ + OfType: a.cons.OfType, + SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, + }).SemanticTokens(ctx)...) + tokens = append(tokens, newExpression(a.pathCtx, eType.FalseResult, schema.AnyExpression{ + OfType: a.cons.OfType, + SkipLiteralComplexTypes: a.cons.SkipLiteralComplexTypes, + }).SemanticTokens(ctx)...) + + return tokens, true + } + + return tokens, false +}