diff --git a/decoder/expr_literal_type.go b/decoder/expr_literal_type.go index 57279383..c1249187 100644 --- a/decoder/expr_literal_type.go +++ b/decoder/expr_literal_type.go @@ -3,6 +3,7 @@ package decoder import ( "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" ) type LiteralType struct { @@ -11,3 +12,19 @@ type LiteralType struct { pathCtx *PathContext } + +func (lt LiteralType) InferType() (cty.Type, bool) { + consType, ok := lt.cons.ConstraintType() + if !ok { + return consType, false + } + + if consType == cty.DynamicPseudoType && !isEmptyExpression(lt.expr) { + val, diags := lt.expr.Value(nil) + if !diags.HasErrors() { + consType = val.Type() + } + } + + return consType, true +} diff --git a/decoder/expr_literal_type_ref_targets.go b/decoder/expr_literal_type_ref_targets.go index e341afbb..a4001946 100644 --- a/decoder/expr_literal_type_ref_targets.go +++ b/decoder/expr_literal_type_ref_targets.go @@ -6,11 +6,19 @@ import ( "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty/cty" ) func (lt LiteralType) ReferenceTargets(ctx context.Context, targetCtx *TargetContext) reference.Targets { typ := lt.cons.Type + if typ == cty.DynamicPseudoType { + val, diags := lt.expr.Value(nil) + if !diags.HasErrors() { + typ = val.Type() + } + } + // Primitive types can only be collected if they have parent address if typ.IsPrimitiveType() && targetCtx != nil && len(targetCtx.ParentAddress) > 0 { return reference.Targets{ diff --git a/decoder/expression.go b/decoder/expression.go index 1a7460d5..593daa1f 100644 --- a/decoder/expression.go +++ b/decoder/expression.go @@ -28,6 +28,10 @@ type ReferenceTargetsExpression interface { ReferenceTargets(ctx context.Context, targetCtx *TargetContext) reference.Targets } +type CanInferTypeExpression interface { + InferType() (cty.Type, bool) +} + // TargetContext describes context for collecting reference targets type TargetContext struct { // FriendlyName is (optional) human-readable name of the expression diff --git a/decoder/reference_targets.go b/decoder/reference_targets.go index 2ce99e67..128b609d 100644 --- a/decoder/reference_targets.go +++ b/decoder/reference_targets.go @@ -701,17 +701,32 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, selfRefBodyRangePtr = content.RangePtr } + rawAttributes, _ := body.JustAttributes() + for name, aSchema := range bodySchema.Attributes { var attrType cty.Type if aSchema.Constraint != nil { var ok bool - cons, ok := aSchema.Constraint.(schema.TypeAwareConstraint) - if !ok { - continue - } - attrType, ok = cons.ConstraintType() - if !ok { - continue + rawAttr, ok := rawAttributes[name] + if ok { + // try to infer type if attribute is declared + expr, ok := newExpression(d.pathCtx, rawAttr.Expr, aSchema.Constraint).(CanInferTypeExpression) + if !ok { + continue + } + attrType, ok = expr.InferType() + if !ok { + continue + } + } else { + cons, ok := aSchema.Constraint.(schema.TypeAwareConstraint) + if !ok { + continue + } + attrType, ok = cons.ConstraintType() + if !ok { + continue + } } } else { var ok bool diff --git a/decoder/reference_targets_collect_hcl_test.go b/decoder/reference_targets_collect_hcl_test.go index ad8055c5..b3786b04 100644 --- a/decoder/reference_targets_collect_hcl_test.go +++ b/decoder/reference_targets_collect_hcl_test.go @@ -4331,9 +4331,9 @@ module "different" { ScopeId: lang.ScopeId("local"), AsExprType: true, }, - Expr: schema.ExprConstraints{ - schema.TraversalExpr{OfType: cty.DynamicPseudoType}, - schema.LiteralTypeExpr{Type: cty.DynamicPseudoType}, + Constraint: schema.OneOf{ + schema.Reference{OfType: cty.DynamicPseudoType}, + schema.LiteralType{Type: cty.DynamicPseudoType}, }, }, }, @@ -4484,7 +4484,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "second"}, + lang.AttrStep{Name: "fourth"}, }, Type: cty.Object(map[string]cty.Type{ "attr": cty.String, @@ -4493,27 +4493,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 6, + Line: 12, Column: 5, - Byte: 66, + Byte: 145, }, End: hcl.Pos{ - Line: 8, + Line: 14, Column: 6, - Byte: 101, + Byte: 180, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 6, + Line: 12, Column: 5, - Byte: 66, + Byte: 145, }, End: hcl.Pos{ - Line: 6, + Line: 12, Column: 11, - Byte: 72, + Byte: 151, }, }, NestedTargets: reference.Targets{ @@ -4521,7 +4521,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "second"}, + lang.AttrStep{Name: "fourth"}, lang.AttrStep{Name: "attr"}, }, Type: cty.String, @@ -4529,27 +4529,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 7, + Line: 13, Column: 7, - Byte: 83, + Byte: 162, }, End: hcl.Pos{ - Line: 7, + Line: 13, Column: 19, - Byte: 95, + Byte: 174, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 7, + Line: 13, Column: 7, - Byte: 83, + Byte: 162, }, End: hcl.Pos{ - Line: 7, + Line: 13, Column: 11, - Byte: 87, + Byte: 166, }, }, }, @@ -4559,7 +4559,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "third"}, + lang.AttrStep{Name: "second"}, }, Type: cty.Object(map[string]cty.Type{ "attr": cty.String, @@ -4568,27 +4568,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 9, + Line: 6, Column: 5, - Byte: 106, + Byte: 66, }, End: hcl.Pos{ - Line: 11, + Line: 8, Column: 6, - Byte: 140, + Byte: 101, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 9, + Line: 6, Column: 5, - Byte: 106, + Byte: 66, }, End: hcl.Pos{ - Line: 9, - Column: 10, - Byte: 111, + Line: 6, + Column: 11, + Byte: 72, }, }, NestedTargets: reference.Targets{ @@ -4596,7 +4596,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "third"}, + lang.AttrStep{Name: "second"}, lang.AttrStep{Name: "attr"}, }, Type: cty.String, @@ -4604,27 +4604,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 10, + Line: 7, Column: 7, - Byte: 122, + Byte: 83, }, End: hcl.Pos{ - Line: 10, + Line: 7, Column: 19, - Byte: 134, + Byte: 95, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 10, + Line: 7, Column: 7, - Byte: 122, + Byte: 83, }, End: hcl.Pos{ - Line: 10, + Line: 7, Column: 11, - Byte: 126, + Byte: 87, }, }, }, @@ -4634,7 +4634,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "fourth"}, + lang.AttrStep{Name: "third"}, }, Type: cty.Object(map[string]cty.Type{ "attr": cty.String, @@ -4643,27 +4643,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 12, + Line: 9, Column: 5, - Byte: 145, + Byte: 106, }, End: hcl.Pos{ - Line: 14, + Line: 11, Column: 6, - Byte: 180, + Byte: 140, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 12, + Line: 9, Column: 5, - Byte: 145, + Byte: 106, }, End: hcl.Pos{ - Line: 12, - Column: 11, - Byte: 151, + Line: 9, + Column: 10, + Byte: 111, }, }, NestedTargets: reference.Targets{ @@ -4671,7 +4671,7 @@ module "different" { Addr: lang.Address{ lang.RootStep{Name: "local"}, lang.AttrStep{Name: "top_obj"}, - lang.AttrStep{Name: "fourth"}, + lang.AttrStep{Name: "third"}, lang.AttrStep{Name: "attr"}, }, Type: cty.String, @@ -4679,27 +4679,27 @@ module "different" { RangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 13, + Line: 10, Column: 7, - Byte: 162, + Byte: 122, }, End: hcl.Pos{ - Line: 13, + Line: 10, Column: 19, - Byte: 174, + Byte: 134, }, }, DefRangePtr: &hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ - Line: 13, + Line: 10, Column: 7, - Byte: 162, + Byte: 122, }, End: hcl.Pos{ - Line: 13, + Line: 10, Column: 11, - Byte: 166, + Byte: 126, }, }, },