Skip to content

Commit 9847e90

Browse files
committed
hcldec must use WithoutOptionalAttributesDeep
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.
1 parent 925bfe8 commit 9847e90

File tree

2 files changed

+43
-16
lines changed

2 files changed

+43
-16
lines changed

hcldec/public_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ func TestDecode(t *testing.T) {
137137
cty.NullVal(cty.Number),
138138
1, // attribute "a" is required
139139
},
140+
{
141+
``,
142+
&AttrSpec{
143+
Name: "a",
144+
Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
145+
"attr": cty.String,
146+
}, []string{"attr"}),
147+
},
148+
nil,
149+
cty.NullVal(cty.Object(map[string]cty.Type{
150+
"attr": cty.String,
151+
})),
152+
0,
153+
},
140154

141155
{
142156
`
@@ -328,6 +342,20 @@ b {
328342
cty.NullVal(cty.Map(cty.String)),
329343
1, // missing b block
330344
},
345+
{
346+
``,
347+
&BlockAttrsSpec{
348+
TypeName: "b",
349+
ElementType: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
350+
"attr": cty.String,
351+
}, []string{"attr"}),
352+
},
353+
nil,
354+
cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
355+
"attr": cty.String,
356+
}))),
357+
0,
358+
},
331359
{
332360
`
333361
b {

hcldec/spec.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,13 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct
200200
if !exists {
201201
// We don't need to check required and emit a diagnostic here, because
202202
// that would already have happened when building "content".
203-
return cty.NullVal(s.Type), nil
203+
return cty.NullVal(s.Type.WithoutOptionalAttributesDeep()), nil
204204
}
205205

206206
if decodeFn := customdecode.CustomExpressionDecoderForType(s.Type); decodeFn != nil {
207207
v, diags := decodeFn(attr.Expr, ctx)
208208
if v == cty.NilVal {
209-
v = cty.UnknownVal(s.Type)
209+
v = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep())
210210
}
211211
return v, diags
212212
}
@@ -229,7 +229,7 @@ func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ct
229229
})
230230
// We'll return an unknown value of the _correct_ type so that the
231231
// incomplete result can still be used for some analysis use-cases.
232-
val = cty.UnknownVal(s.Type)
232+
val = cty.UnknownVal(s.Type.WithoutOptionalAttributesDeep())
233233
} else {
234234
val = convVal
235235
}
@@ -381,7 +381,7 @@ func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, c
381381
Subject: &content.MissingItemRange,
382382
})
383383
}
384-
return cty.NullVal(s.Nested.impliedType()), diags
384+
return cty.NullVal(s.Nested.impliedType().WithoutOptionalAttributesDeep()), diags
385385
}
386386

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

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

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

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

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

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

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

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

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

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

16801679
validateDiags := s.Func(wrappedVal)

0 commit comments

Comments
 (0)