Skip to content

Commit

Permalink
hcldec must use WithoutOptionalAttributesDeep
Browse files Browse the repository at this point in the history
When hcldec needs to return a synthetic value, it must use
WithoutOptionalAttributesDeep to ensure the correct type for Null or
Unknown values. Optional attributes are normally removed from the value
type when decoding, but since hcldec is dealing with the decoder spec
directly, the optional attr types are still going to be present in these
cases.
  • Loading branch information
jbardin committed Oct 17, 2023
1 parent 925bfe8 commit 9847e90
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 16 deletions.
28 changes: 28 additions & 0 deletions hcldec/public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ func TestDecode(t *testing.T) {
cty.NullVal(cty.Number),
1, // attribute "a" is required
},
{
``,
&AttrSpec{
Name: "a",
Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"attr": cty.String,
}, []string{"attr"}),
},
nil,
cty.NullVal(cty.Object(map[string]cty.Type{
"attr": cty.String,
})),
0,
},

{
`
Expand Down Expand Up @@ -328,6 +342,20 @@ b {
cty.NullVal(cty.Map(cty.String)),
1, // missing b block
},
{
``,
&BlockAttrsSpec{
TypeName: "b",
ElementType: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"attr": cty.String,
}, []string{"attr"}),
},
nil,
cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
"attr": cty.String,
}))),
0,
},
{
`
b {
Expand Down
31 changes: 15 additions & 16 deletions hcldec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,13 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct
if !exists {
// We don't need to check required and emit a diagnostic here, because
// that would already have happened when building "content".
return cty.NullVal(s.Type), nil
return cty.NullVal(s.Type.WithoutOptionalAttributesDeep()), nil
}

if decodeFn := customdecode.CustomExpressionDecoderForType(s.Type); decodeFn != nil {
v, diags := decodeFn(attr.Expr, ctx)
if v == cty.NilVal {
v = cty.UnknownVal(s.Type)
v = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep())
}
return v, diags
}
Expand All @@ -229,7 +229,7 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct
})
// We'll return an unknown value of the _correct_ type so that the
// incomplete result can still be used for some analysis use-cases.
val = cty.UnknownVal(s.Type)
val = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep())
} else {
val = convVal
}
Expand Down Expand Up @@ -381,7 +381,7 @@ func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, c
Subject: &content.MissingItemRange,
})
}
return cty.NullVal(s.Nested.impliedType()), diags
return cty.NullVal(s.Nested.impliedType().WithoutOptionalAttributesDeep()), diags
}

if s.Nested == nil {
Expand Down Expand Up @@ -478,7 +478,7 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe
if u.Unknown() {
// If any block Body is unknown, then the entire block value
// must be unknown
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}
}

Expand Down Expand Up @@ -640,7 +640,7 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab
if u.Unknown() {
// If any block Body is unknown, then the entire block value
// must be unknown
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}
}

Expand Down Expand Up @@ -763,7 +763,7 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
if u.Unknown() {
// If any block Body is unknown, then the entire block value
// must be unknown
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}
}

Expand Down Expand Up @@ -922,7 +922,7 @@ func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
if u.Unknown() {
// If any block Body is unknown, then the entire block value
// must be unknown
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}
}

Expand Down Expand Up @@ -1076,7 +1076,7 @@ func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLa
if u.Unknown() {
// If any block Body is unknown, then the entire block value
// must be unknown
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}
}

Expand Down Expand Up @@ -1250,7 +1250,7 @@ func (s *BlockAttrsSpec) decode(content *hcl.BodyContent, blockLabels []blockLab
Subject: &content.MissingItemRange,
})
}
return cty.NullVal(cty.Map(s.ElementType)), diags
return cty.NullVal(cty.Map(s.ElementType).WithoutOptionalAttributesDeep()), diags
}
if other != nil {
diags = append(diags, &hcl.Diagnostic{
Expand Down Expand Up @@ -1513,7 +1513,7 @@ func (s *TransformExprSpec) decode(content *hcl.BodyContent, blockLabels []block
// We won't try to run our function in this case, because it'll probably
// generate confusing additional errors that will distract from the
// root cause.
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}

chiCtx := s.TransformCtx.NewChild()
Expand Down Expand Up @@ -1569,7 +1569,7 @@ func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []block
// We won't try to run our function in this case, because it'll probably
// generate confusing additional errors that will distract from the
// root cause.
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}

resultVal, err := s.Func.Call([]cty.Value{wrappedVal})
Expand All @@ -1583,7 +1583,7 @@ func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []block
Detail: fmt.Sprintf("Decoder transform returned an error: %s", err),
Subject: s.sourceRange(content, blockLabels).Ptr(),
})
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}

return resultVal, diags
Expand Down Expand Up @@ -1637,7 +1637,7 @@ func (s *RefineValueSpec) decode(content *hcl.BodyContent, blockLabels []blockLa
// We won't try to run our function in this case, because it'll probably
// generate confusing additional errors that will distract from the
// root cause.
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}

return wrappedVal.RefineWith(s.Refine), diags
Expand All @@ -1658,7 +1658,6 @@ func (s *RefineValueSpec) sourceRange(content *hcl.BodyContent, blockLabels []bl
// The Subject field of the returned Diagnostic is optional. If not
// specified, it is automatically populated with the range covered by
// the wrapped spec.
//
type ValidateSpec struct {
Wrapped Spec
Func func(value cty.Value) hcl.Diagnostics
Expand All @@ -1674,7 +1673,7 @@ func (s *ValidateSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
// We won't try to run our function in this case, because it'll probably
// generate confusing additional errors that will distract from the
// root cause.
return cty.UnknownVal(s.impliedType()), diags
return cty.UnknownVal(s.impliedType().WithoutOptionalAttributesDeep()), diags
}

validateDiags := s.Func(wrappedVal)
Expand Down

0 comments on commit 9847e90

Please sign in to comment.