diff --git a/internal/command/jsonformat/differ/attribute.go b/internal/command/jsonformat/differ/attribute.go index 48bb90b61a..56c4272651 100644 --- a/internal/command/jsonformat/differ/attribute.go +++ b/internal/command/jsonformat/differ/attribute.go @@ -49,6 +49,12 @@ func (v Value) computeChangeForType(ctype cty.Type) change.Change { switch { case ctype == cty.NilType, ctype == cty.DynamicPseudoType: + // Forward nil or dynamic types over to be processed as outputs. + // There is nothing particularly special about the way outputs are + // processed that make this unsafe, we could just as easily call this + // function computeChangeForDynamicValues(), but external callers will + // only be in this situation when processing outputs so this function + // is named for their benefit. return v.ComputeChangeForOutput() case ctype.IsPrimitiveType(): return v.computeAttributeChangeAsPrimitive(ctype) diff --git a/internal/command/jsonformat/differ/value_test.go b/internal/command/jsonformat/differ/value_test.go index 716f6d0414..75d1dac7ac 100644 --- a/internal/command/jsonformat/differ/value_test.go +++ b/internal/command/jsonformat/differ/value_test.go @@ -1573,6 +1573,33 @@ func TestValue_PrimitiveAttributes(t *testing.T) { attribute: cty.String, validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"old\""), plans.NoOp, false), }, + "dynamic": { + input: Value{ + Before: "old", + After: "new", + }, + attribute: cty.DynamicPseudoType, + validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, false), + validateSliceChanges: []change.ValidateChangeFunc{ + change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false), + change.ValidatePrimitive(nil, strptr("\"new\""), plans.Create, false), + }, + }, + "dynamic_type_change": { + input: Value{ + Before: "old", + After: 4.0, + }, + attribute: cty.DynamicPseudoType, + validateChange: change.ValidateTypeChange( + change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false), + change.ValidatePrimitive(nil, strptr("4"), plans.Create, false), + plans.Update, false), + validateSliceChanges: []change.ValidateChangeFunc{ + change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false), + change.ValidatePrimitive(nil, strptr("4"), plans.Create, false), + }, + }, } for name, tmp := range tcs { tc := tmp