diff --git a/internal/deprecation/deprecation.go b/internal/deprecation/deprecation.go index f7065680a6..a176c4ca00 100644 --- a/internal/deprecation/deprecation.go +++ b/internal/deprecation/deprecation.go @@ -54,22 +54,19 @@ func (d *Deprecations) ValidateAndUnmark(value cty.Value, module addrs.Module, r // It finds the most specific range possible for each diagnostic. func (d *Deprecations) ValidateExpressionDeepAndUnmark(value cty.Value, module addrs.Module, expr hcl.Expression) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - unmarked, pvms := value.UnmarkDeepWithPaths() + undeprecatedVal, pdms := marks.GetDeprecationMarksDeep(value) // Check if we need to suppress deprecation warnings for this module call. if d.IsModuleCallDeprecationSuppressed(module) { - return unmarked.MarkWithPaths(marks.RemoveAll(pvms, marks.Deprecation)), diags + return undeprecatedVal, diags } - for _, pvm := range pvms { - for m := range pvm.Marks { - if depMark, ok := m.(marks.DeprecationMark); ok { - rng := tfdiags.RangeForExpressionAtPath(expr, pvm.Path) - diags = diags.Append(deprecationMarkToDiagnostic(depMark, &rng)) - } - } + for _, pdm := range pdms { + rng := tfdiags.RangeForExpressionAtPath(expr, pdm.Path) + diags = diags.Append(deprecationMarkToDiagnostic(pdm.Mark, &rng)) } - return unmarked.MarkWithPaths(marks.RemoveAll(pvms, marks.Deprecation)), diags + + return undeprecatedVal, diags } func (d *Deprecations) deprecationMarksToDiagnostics(deprecationMarks []marks.DeprecationMark, module addrs.Module, rng *hcl.Range) tfdiags.Diagnostics { @@ -109,41 +106,34 @@ func deprecationMarkToDiagnostic(depMark marks.DeprecationMark, subject *hcl.Ran // unless deprecation warnings are suppressed for the given module. func (d *Deprecations) ValidateAndUnmarkConfig(value cty.Value, schema *configschema.Block, module addrs.Module) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - unmarked, pvms := value.UnmarkDeepWithPaths() + undeprecatedVal, pdms := marks.GetDeprecationMarksDeep(value) if d.IsModuleCallDeprecationSuppressed(module) { // Even if we don't want to get deprecation warnings we want to remove the marks - return unmarked.MarkWithPaths(marks.RemoveAll(pvms, marks.Deprecation)), diags + return undeprecatedVal, diags } - for _, pvm := range pvms { - for m := range pvm.Marks { - if depMark, ok := m.(marks.DeprecationMark); ok { - diag := tfdiags.AttributeValue( - tfdiags.Warning, - "Deprecated value used", - depMark.Message, - pvm.Path, - ) - if depMark.OriginDescription != "" { - diag = tfdiags.Override( - diag, - tfdiags.Warning, // We just want to override the extra info - func() tfdiags.DiagnosticExtraWrapper { - return &tfdiags.DeprecationOriginDiagnosticExtra{ - // TODO: Remove common prefixes from origin descriptions? - OriginDescription: depMark.OriginDescription, - } - }) - } - - diags = diags.Append(diag) - - } + for _, pdm := range pdms { + diag := tfdiags.AttributeValue( + tfdiags.Warning, + "Deprecated value used", + pdm.Mark.Message, + pdm.Path, + ) + if pdm.Mark.OriginDescription != "" { + diag = tfdiags.Override( + diag, + tfdiags.Warning, // We just want to override the extra info + func() tfdiags.DiagnosticExtraWrapper { + return &tfdiags.DeprecationOriginDiagnosticExtra{ + OriginDescription: pdm.Mark.OriginDescription, + } + }) } + diags = diags.Append(diag) } - return unmarked.MarkWithPaths(marks.RemoveAll(pvms, marks.Deprecation)), diags + return undeprecatedVal, diags } func (d *Deprecations) IsModuleCallDeprecationSuppressed(addr addrs.Module) bool { diff --git a/internal/lang/marks/marks.go b/internal/lang/marks/marks.go index 7639419e2f..d2aca10924 100644 --- a/internal/lang/marks/marks.go +++ b/internal/lang/marks/marks.go @@ -5,6 +5,7 @@ package marks import ( "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/ctymarks" ) // valueMarks allow creating strictly typed values for use as cty.Value marks. @@ -24,12 +25,11 @@ func Has(val cty.Value, mark interface{}) bool { // For value marks Has returns true if a mark of the type is present case DeprecationMark: - for depMark := range val.Marks() { - if _, ok := depMark.(DeprecationMark); ok { - return true - } + for range cty.ValueMarksOfType[DeprecationMark](val) { + return true } return false + default: panic("Unknown mark type") } @@ -72,26 +72,30 @@ func GetDeprecationMarks(val cty.Value) (cty.Value, []DeprecationMark) { return unmarked.WithMarks(other), depMarks } -// RemoveDeprecationMarks returns a copy of the given cty.Value with all -// deprecation marks removed. -func RemoveDeprecationMarks(val cty.Value) cty.Value { - newVal, marks := val.Unmark() +type PathDeprecationMark struct { + Mark DeprecationMark + Path cty.Path +} - for mark := range marks { - if _, ok := mark.(DeprecationMark); !ok { - newVal = newVal.Mark(mark) +// GetDeprecationMarksDeep returns a copy of the given cty.Value with all +// deprecation marks removed, along with a slice of all deprecation marks found +// in the value and their paths. +func GetDeprecationMarksDeep(value cty.Value) (cty.Value, []PathDeprecationMark) { + pdms := []PathDeprecationMark{} + undeprecatedVal, _ := value.WrangleMarksDeep(func(mark any, path cty.Path) (ctymarks.WrangleAction, error) { + if depMark, ok := mark.(DeprecationMark); ok { + pdms = append(pdms, PathDeprecationMark{ + Mark: depMark, + Path: path.Copy(), + }) + // We want to drop the deprecation marks + return ctymarks.WrangleDrop, nil } - } - - return newVal -} + // and ignore all other marks + return ctymarks.WrangleKeep, nil + }) -// RemoveDeprecationMarksDeep returns a copy of the given cty.Value with all -// deprecation marks deeply removed. -func RemoveDeprecationMarksDeep(val cty.Value) cty.Value { - newVal, pvms := val.UnmarkDeepWithPaths() - otherPvms := RemoveAll(pvms, Deprecation) - return newVal.MarkWithPaths(otherPvms) + return undeprecatedVal, pdms } // Sensitive indicates that this value is marked as sensitive in the context of diff --git a/internal/terraform/eval_for_each.go b/internal/terraform/eval_for_each.go index 8f0951d1b4..e6eb4535c6 100644 --- a/internal/terraform/eval_for_each.go +++ b/internal/terraform/eval_for_each.go @@ -90,7 +90,7 @@ func (ev *forEachEvaluator) ResourceValue() (map[string]cty.Value, bool, tfdiags return res, false, diags } - forEachVal = marks.RemoveDeprecationMarks(forEachVal) + forEachVal, _ = marks.GetDeprecationMarks(forEachVal) if forEachVal.IsNull() || !forEachVal.IsKnown() || markSafeLengthInt(forEachVal) == 0 { // we check length, because an empty set returns a nil map which will panic below return res, true, diags diff --git a/internal/terraform/node_output.go b/internal/terraform/node_output.go index d9d32f4961..ddb3e22646 100644 --- a/internal/terraform/node_output.go +++ b/internal/terraform/node_output.go @@ -521,7 +521,7 @@ If you do intend to export this data, annotate the output value as sensitive by } if n.Config.DeprecatedSet { - val = marks.RemoveDeprecationMarksDeep(val) + val, _ = marks.GetDeprecationMarksDeep(val) if n.Addr.Module.IsRoot() { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError,