Apply some new go collection features (#36818)

This commit is contained in:
Samsondeen 2025-04-02 16:20:16 +02:00 committed by GitHub
parent 7fadbe34de
commit c0a7ff235b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 128 additions and 381 deletions

View file

@ -13,6 +13,8 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"maps"
"github.com/hashicorp/terraform/internal/backend/backendrun"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/configs/configload"
@ -400,9 +402,8 @@ func (b *Local) interactiveCollectVariables(ctx context.Context, existing map[st
// variable's value.
sort.Strings(needed) // prompt in lexical order
ret := make(map[string]backendrun.UnparsedVariableValue, len(vcs))
for k, v := range existing {
ret[k] = v
}
maps.Copy(ret, existing) // don't use clone here, so we can have a non-nil map
for _, name := range needed {
vc := vcs[name]
query := fmt.Sprintf("var.%s", name)
@ -467,9 +468,8 @@ func (b *Local) stubUnsetRequiredVariables(existing map[string]backendrun.Unpars
// If we get down here then there's at least one variable value to add.
ret := make(map[string]backendrun.UnparsedVariableValue, len(vcs))
for k, v := range existing {
ret[k] = v
}
maps.Copy(ret, existing) // don't use clone here, so we can return a non-nil map
for name, vc := range vcs {
if !vc.Required() {
continue

View file

@ -8,10 +8,12 @@ import (
"fmt"
"log"
"path/filepath"
"sort"
"slices"
"github.com/zclconf/go-cty/cty"
"maps"
"github.com/hashicorp/terraform/internal/backend/backendrun"
"github.com/hashicorp/terraform/internal/command/junit"
"github.com/hashicorp/terraform/internal/command/views"
@ -94,28 +96,18 @@ func (runner *TestSuiteRunner) Test() (moduletest.Status, tfdiags.Diagnostics) {
runner.View.Abstract(suite)
var files []string
for name := range suite.Files {
files = append(files, name)
}
sort.Strings(files) // execute the files in alphabetical order
// We have two sets of variables that are available to different test files.
// Test files in the root directory have access to the GlobalVariables only,
// while test files in the test directory have access to the union of
// GlobalVariables and GlobalTestVariables.
testDirectoryGlobalVariables := make(map[string]backendrun.UnparsedVariableValue)
for name, value := range runner.GlobalVariables {
testDirectoryGlobalVariables[name] = value
}
for name, value := range runner.GlobalTestVariables {
// We're okay to overwrite the global variables in case of name
// collisions, as the test directory variables should take precedence.
testDirectoryGlobalVariables[name] = value
}
maps.Copy(testDirectoryGlobalVariables, runner.GlobalVariables)
// We're okay to overwrite the global variables in case of name
// collisions, as the test directory variables should take precedence.
maps.Copy(testDirectoryGlobalVariables, runner.GlobalTestVariables)
suite.Status = moduletest.Pass
for _, name := range files {
for _, name := range slices.Sorted(maps.Keys(suite.Files)) {
if runner.Cancelled {
return suite.Status, diags
}
@ -144,9 +136,7 @@ func (runner *TestSuiteRunner) Test() (moduletest.Status, tfdiags.Diagnostics) {
}
evalCtx.VariableCaches = hcltest.NewVariableCaches(func(vc *hcltest.VariableCaches) {
for name, value := range currentGlobalVariables {
vc.GlobalVariables[name] = value
}
maps.Copy(vc.GlobalVariables, currentGlobalVariables)
vc.FileVariables = file.Config.Variables
})
fileRunner := &TestFileRunner{

View file

@ -26,6 +26,8 @@ import (
"github.com/hashicorp/terraform/internal/states/remote"
"github.com/hashicorp/terraform/internal/states/statemgr"
"maps"
coordinationv1 "k8s.io/api/coordination/v1"
coordinationclientv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
)
@ -351,12 +353,7 @@ func (c *RemoteClient) getLabels() map[string]string {
tfstateWorkspaceKey: c.workspace,
managedByKey: "terraform",
}
if len(c.labels) != 0 {
for k, v := range c.labels {
l[k] = v
}
}
maps.Copy(l, c.labels)
return l
}

View file

@ -17,6 +17,7 @@ import (
"io/fs"
"io/ioutil"
"log"
"maps"
"os"
"path/filepath"
"strings"
@ -341,18 +342,14 @@ func (c *Config) Merge(c2 *Config) *Config {
var result Config
result.Providers = make(map[string]string)
result.Provisioners = make(map[string]string)
for k, v := range c.Providers {
result.Providers[k] = v
}
maps.Copy(result.Providers, c.Providers)
maps.Copy(result.Provisioners, c.Provisioners)
for k, v := range c2.Providers {
if v1, ok := c.Providers[k]; ok {
log.Printf("[INFO] Local %s provider configuration '%s' overrides '%s'", k, v, v1)
}
result.Providers[k] = v
}
for k, v := range c.Provisioners {
result.Provisioners[k] = v
}
for k, v := range c2.Provisioners {
if v1, ok := c.Provisioners[k]; ok {
log.Printf("[INFO] Local %s provisioner configuration '%s' overrides '%s'", k, v, v1)
@ -375,35 +372,23 @@ func (c *Config) Merge(c2 *Config) *Config {
if (len(c.Hosts) + len(c2.Hosts)) > 0 {
result.Hosts = make(map[string]*ConfigHost)
for name, host := range c.Hosts {
result.Hosts[name] = host
}
for name, host := range c2.Hosts {
result.Hosts[name] = host
}
maps.Copy(result.Hosts, c.Hosts)
maps.Copy(result.Hosts, c2.Hosts)
}
if (len(c.Credentials) + len(c2.Credentials)) > 0 {
result.Credentials = make(map[string]map[string]interface{})
for host, creds := range c.Credentials {
result.Credentials[host] = creds
}
for host, creds := range c2.Credentials {
// We just clobber an entry from the other file right now. Will
// improve on this later using the more-robust merging behavior
// built in to HCL2.
result.Credentials[host] = creds
}
maps.Copy(result.Credentials, c.Credentials)
// We just clobber an entry from the other file right now. Will
// improve on this later using the more-robust merging behavior
// built in to HCL2.
maps.Copy(result.Credentials, c2.Credentials)
}
if (len(c.CredentialsHelpers) + len(c2.CredentialsHelpers)) > 0 {
result.CredentialsHelpers = make(map[string]*ConfigCredentialsHelper)
for name, helper := range c.CredentialsHelpers {
result.CredentialsHelpers[name] = helper
}
for name, helper := range c2.CredentialsHelpers {
result.CredentialsHelpers[name] = helper
}
maps.Copy(result.CredentialsHelpers, c.CredentialsHelpers)
maps.Copy(result.CredentialsHelpers, c2.CredentialsHelpers)
}
if (len(c.ProviderInstallation) + len(c2.ProviderInstallation)) > 0 {

View file

@ -8,6 +8,7 @@ import (
"bytes"
"fmt"
"iter"
"slices"
"sort"
"strings"
@ -300,8 +301,7 @@ func (f *snippetFormatter) write() {
// This is particularly useful for expressions that get evaluated
// multiple times with different values, such as blocks using
// "count" and "for_each", or within "for" expressions.
values := make([]viewsjson.DiagnosticExpressionValue, len(snippet.Values))
copy(values, snippet.Values)
values := slices.Clone(snippet.Values)
sort.Slice(values, func(i, j int) bool {
return values[i].Traversal < values[j].Traversal
})

View file

@ -4,7 +4,8 @@
package jsonformat
import (
"sort"
"maps"
"slices"
ctyjson "github.com/zclconf/go-cty/cty/json"
@ -88,13 +89,7 @@ func (state State) renderHumanStateOutputs(renderer Renderer, opts computed.Rend
if len(state.RootModuleOutputs) > 0 {
renderer.Streams.Printf("\n\nOutputs:\n\n")
var keys []string
for key := range state.RootModuleOutputs {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
for _, key := range slices.Sorted(maps.Keys(state.RootModuleOutputs)) {
output := state.RootModuleOutputs[key]
change := structured.FromJsonOutput(output)
ctype, err := ctyjson.UnmarshalType(output.Type)

View file

@ -6,6 +6,8 @@ package jsonstate
import (
"encoding/json"
"fmt"
"maps"
"slices"
"sort"
"github.com/zclconf/go-cty/cty"
@ -477,13 +479,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
ret = append(ret, current)
}
var sortedDeposedKeys []string
for k := range ri.Deposed {
sortedDeposedKeys = append(sortedDeposedKeys, string(k))
}
sort.Strings(sortedDeposedKeys)
for _, deposedKey := range sortedDeposedKeys {
for _, deposedKey := range slices.Sorted(maps.Keys(ri.Deposed)) {
rios := ri.Deposed[states.DeposedKey(deposedKey)]
// copy the base fields from the current instance
@ -531,7 +527,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
if riObj.Status == states.ObjectTainted {
deposed.Tainted = true
}
deposed.DeposedKey = deposedKey
deposed.DeposedKey = string(deposedKey)
ret = append(ret, deposed)
}
}

View file

@ -6,6 +6,7 @@ import (
"bytes"
"encoding/xml"
"fmt"
"maps"
"os"
"slices"
"strconv"
@ -155,8 +156,9 @@ func junitXMLTestReport(suite *moduletest.Suite, suiteRunnerStopped bool, source
enc.EncodeToken(xml.StartElement{Name: suitesName})
sortedFiles := suiteFilesAsSortedList(suite.Files) // to ensure consistent ordering in XML
for _, file := range sortedFiles {
// Sort the file names to ensure consistent ordering in XML
for _, name := range slices.Sorted(maps.Keys(suite.Files)) {
file := suite.Files[name]
// Each test file is modelled as a "test suite".
// First we'll count the number of tests and number of failures/errors
@ -327,22 +329,6 @@ func skipDetails(runIndex int, file *moduletest.File, suiteStopped bool) *withMe
return &withMessage{}
}
func suiteFilesAsSortedList(files map[string]*moduletest.File) []*moduletest.File {
fileNames := make([]string, len(files))
i := 0
for k := range files {
fileNames[i] = k
i++
}
slices.Sort(fileNames)
sortedFiles := make([]*moduletest.File, len(files))
for i, name := range fileNames {
sortedFiles[i] = files[name]
}
return sortedFiles
}
func getDiagString(diags tfdiags.Diagnostics, sources map[string][]byte) string {
var diagsStr strings.Builder
for _, d := range diags {

View file

@ -7,8 +7,6 @@ import (
"fmt"
"os"
"testing"
"github.com/hashicorp/terraform/internal/moduletest"
)
func Test_TestJUnitXMLFile_save(t *testing.T) {
@ -66,88 +64,3 @@ func Test_TestJUnitXMLFile_save(t *testing.T) {
})
}
}
func Test_suiteFilesAsSortedList(t *testing.T) {
cases := map[string]struct {
Suite *moduletest.Suite
ExpectedNames map[int]string
}{
"no test files": {
Suite: &moduletest.Suite{},
},
"3 test files ordered in map": {
Suite: &moduletest.Suite{
Status: moduletest.Skip,
Files: map[string]*moduletest.File{
"test_file_1.tftest.hcl": {
Name: "test_file_1.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_2.tftest.hcl": {
Name: "test_file_2.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_3.tftest.hcl": {
Name: "test_file_3.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
},
},
ExpectedNames: map[int]string{
0: "test_file_1.tftest.hcl",
1: "test_file_2.tftest.hcl",
2: "test_file_3.tftest.hcl",
},
},
"3 test files unordered in map": {
Suite: &moduletest.Suite{
Status: moduletest.Skip,
Files: map[string]*moduletest.File{
"test_file_3.tftest.hcl": {
Name: "test_file_3.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_1.tftest.hcl": {
Name: "test_file_1.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
"test_file_2.tftest.hcl": {
Name: "test_file_2.tftest.hcl",
Status: moduletest.Skip,
Runs: []*moduletest.Run{},
},
},
},
ExpectedNames: map[int]string{
0: "test_file_1.tftest.hcl",
1: "test_file_2.tftest.hcl",
2: "test_file_3.tftest.hcl",
},
},
}
for tn, tc := range cases {
t.Run(tn, func(t *testing.T) {
list := suiteFilesAsSortedList(tc.Suite.Files)
if len(tc.ExpectedNames) != len(tc.Suite.Files) {
t.Fatalf("expected there to be %d items, got %d", len(tc.ExpectedNames), len(tc.Suite.Files))
}
if len(tc.ExpectedNames) == 0 {
return
}
for k, v := range tc.ExpectedNames {
if list[k].Name != v {
t.Fatalf("expected element %d in sorted list to be named %s, got %s", k, v, list[k].Name)
}
}
})
}
}

View file

@ -11,6 +11,7 @@ import (
"fmt"
"io/ioutil"
"log"
"maps"
"os"
"path/filepath"
"strconv"
@ -293,9 +294,7 @@ func (m *Meta) StateOutPath() string {
// Colorize returns the colorization structure for a command.
func (m *Meta) Colorize() *colorstring.Colorize {
colors := make(map[string]string)
for k, v := range colorstring.DefaultColors {
colors[k] = v
}
maps.Copy(colors, colorstring.DefaultColors)
colors["purple"] = "38;5;57"
return &colorstring.Colorize{

View file

@ -6,7 +6,10 @@ package configs
import (
"fmt"
"log"
"maps"
"slices"
"sort"
"strings"
version "github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
@ -135,13 +138,8 @@ func (c *Config) Depth() int {
func (c *Config) DeepEach(cb func(c *Config)) {
cb(c)
names := make([]string, 0, len(c.Children))
for name := range c.Children {
names = append(names, name)
}
for _, name := range names {
c.Children[name].DeepEach(cb)
for _, ch := range c.Children {
ch.DeepEach(cb)
}
}
@ -826,12 +824,8 @@ func (c *Config) ProviderTypes() []addrs.Provider {
// Ignore diagnostics here because they relate to version constraints
reqs, _ := c.ProviderRequirements()
ret := make([]addrs.Provider, 0, len(reqs))
for k := range reqs {
ret = append(ret, k)
}
sort.Slice(ret, func(i, j int) bool {
return ret[i].String() < ret[j].String()
ret := slices.SortedFunc(maps.Keys(reqs), func(i, j addrs.Provider) int {
return strings.Compare(i.String(), j.String())
})
return ret
}

View file

@ -5,8 +5,9 @@ package configs
import (
"fmt"
"maps"
"path"
"sort"
"slices"
"strings"
version "github.com/hashicorp/go-version"
@ -150,17 +151,10 @@ func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config,
// We'll sort the calls by their local names so that they'll appear in a
// predictable order in any logging that's produced during the walk.
callNames := make([]string, 0, len(calls))
for k := range calls {
callNames = append(callNames, k)
}
sort.Strings(callNames)
for _, callName := range callNames {
for _, callName := range slices.Sorted(maps.Keys(calls)) {
call := calls[callName]
path := make([]string, len(parent.Path)+1)
copy(path, parent.Path)
path[len(path)-1] = call.Name
path := slices.Clone(parent.Path)
path = append(path, call.Name)
req := ModuleRequest{
Name: call.Name,

View file

@ -6,9 +6,10 @@ package configload
import (
"fmt"
"io"
"maps"
"os"
"path/filepath"
"sort"
"slices"
"time"
version "github.com/hashicorp/go-version"
@ -257,11 +258,7 @@ func (fs snapshotFS) Open(name string) (afero.File, error) {
modDir := filepath.Clean(candidate.Dir)
if modDir == directDir {
// We've matched the module directory itself
filenames := make([]string, 0, len(candidate.Files))
for n := range candidate.Files {
filenames = append(filenames, n)
}
sort.Strings(filenames)
filenames := slices.Sorted(maps.Keys(candidate.Files))
return &snapshotDir{
filenames: filenames,
}, nil

View file

@ -6,6 +6,8 @@ package dag
import (
"bytes"
"fmt"
"maps"
"slices"
"sort"
"strings"
)
@ -97,13 +99,8 @@ func (v *marshalVertex) dot(g *marshalGraph, opts *DotOpts) []byte {
return []byte{}
}
newAttrs := make(map[string]string)
for k, v := range attrs {
newAttrs[k] = v
}
for k, v := range node.Attrs {
newAttrs[k] = v
}
newAttrs := maps.Clone(attrs)
maps.Copy(newAttrs, node.Attrs)
name = node.Name
attrs = newAttrs
@ -218,13 +215,7 @@ func (g *marshalGraph) writeBody(opts *DotOpts, w *indentWriter) {
}
// sort these again to match the old output
dotEdgesList := make([]string, 0, len(dotEdges))
for _, v := range dotEdges {
dotEdgesList = append(dotEdgesList, v)
}
sort.Strings(dotEdgesList)
for _, e := range dotEdgesList {
for _, e := range slices.Sorted(maps.Values(dotEdges)) {
w.WriteString(e + "\n")
}

View file

@ -3,6 +3,11 @@
package dag
import (
"iter"
"maps"
)
// Set is a set data structure.
type Set map[interface{}]interface{}
@ -92,25 +97,18 @@ func (s Set) Len() int {
return len(s)
}
// List returns the list of set elements.
func (s Set) List() []interface{} {
if s == nil {
return nil
// List returns the sequence of set elements.
func (s Set) List() iter.Seq[any] {
return func(yield func(any) bool) {
for _, v := range s {
if !yield(v) {
return
}
}
}
r := make([]interface{}, 0, len(s))
for _, v := range s {
r = append(r, v)
}
return r
}
// Copy returns a shallow copy of the set.
func (s Set) Copy() Set {
c := make(Set, len(s))
for k, v := range s {
c[k] = v
}
return c
return maps.Clone(s)
}

View file

@ -5,8 +5,11 @@ package depsfile
import (
"fmt"
"slices"
"sort"
"maps"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/getproviders/providerreqs"
)
@ -69,11 +72,7 @@ func (l *Locks) Provider(addr addrs.Provider) *ProviderLock {
func (l *Locks) AllProviders() map[addrs.Provider]*ProviderLock {
// We return a copy of our internal map so that future calls to
// SetProvider won't modify the map we're returning, or vice-versa.
ret := make(map[addrs.Provider]*ProviderLock, len(l.providers))
for k, v := range l.providers {
ret[k] = v
}
return ret
return maps.Clone(l.providers)
}
// SetProvider creates a new lock or replaces the existing lock for the given
@ -315,11 +314,7 @@ func (l *Locks) Empty() bool {
func (l *Locks) DeepCopy() *Locks {
ret := NewLocks()
for addr, lock := range l.providers {
var hashes []providerreqs.Hash
if len(lock.hashes) > 0 {
hashes = make([]providerreqs.Hash, len(lock.hashes))
copy(hashes, lock.hashes)
}
hashes := slices.Clone(lock.hashes)
ret.SetProvider(addr, lock.version, lock.versionConstraints, hashes)
}
return ret

View file

@ -5,6 +5,8 @@ package depsfile
import (
"fmt"
"maps"
"slices"
"sort"
"github.com/hashicorp/hcl/v2"
@ -138,10 +140,7 @@ func SaveLocksToBytes(locks *Locks) ([]byte, tfdiags.Diagnostics) {
},
})
providers := make([]addrs.Provider, 0, len(locks.providers))
for provider := range locks.providers {
providers = append(providers, provider)
}
providers := slices.Collect(maps.Keys(locks.providers))
sort.Slice(providers, func(i, j int) bool {
return providers[i].LessThan(providers[j])
})

View file

@ -6,7 +6,8 @@ package genconfig
import (
"encoding/json"
"fmt"
"sort"
"maps"
"slices"
"strings"
"github.com/hashicorp/hcl/v2"
@ -72,14 +73,7 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
}
// Get a list of sorted attribute names so the output will be consistent between runs.
keys := make([]string, 0, len(attrs))
for k := range attrs {
keys = append(keys, k)
}
sort.Strings(keys)
for i := range keys {
name := keys[i]
for _, name := range slices.Sorted(maps.Keys(attrs)) {
attrS := attrs[name]
if attrS.NestedType != nil {
diags = diags.Append(writeConfigNestedTypeAttribute(addr, buf, name, attrS, indent))
@ -124,15 +118,8 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
return diags
}
// Get a list of sorted attribute names so the output will be consistent between runs.
keys := make([]string, 0, len(attrs))
for k := range attrs {
keys = append(keys, k)
}
sort.Strings(keys)
for i := range keys {
name := keys[i]
// Sort attribute names so the output will be consistent between runs.
for _, name := range slices.Sorted(maps.Keys(attrs)) {
attrS := attrs[name]
if attrS.NestedType != nil {
writeConfigNestedTypeAttributeFromExisting(addr, buf, name, attrS, stateVal, indent)
@ -233,14 +220,7 @@ func writeConfigBlocks(addr addrs.AbsResourceInstance, buf *strings.Builder, blo
}
// Get a list of sorted block names so the output will be consistent between runs.
names := make([]string, 0, len(blocks))
for k := range blocks {
names = append(names, k)
}
sort.Strings(names)
for i := range names {
name := names[i]
for _, name := range slices.Sorted(maps.Keys(blocks)) {
blockS := blocks[name]
diags = diags.Append(writeConfigNestedBlock(addr, buf, name, blockS, indent))
}
@ -329,14 +309,8 @@ func writeConfigBlocksFromExisting(addr addrs.AbsResourceInstance, buf *strings.
return diags
}
// Get a list of sorted block names so the output will be consistent between runs.
names := make([]string, 0, len(blocks))
for k := range blocks {
names = append(names, k)
}
sort.Strings(names)
for _, name := range names {
// Sort block names so the output will be consistent between runs.
for _, name := range slices.Sorted(maps.Keys(blocks)) {
blockS := blocks[name]
// This shouldn't happen in real usage; state always has all values (set
// to null as needed), but it protects against panics in tests (and any
@ -436,15 +410,10 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
}
vals := attr.AsValueMap()
keys := make([]string, 0, len(vals))
for key := range vals {
keys = append(keys, key)
}
sort.Strings(keys)
buf.WriteString(strings.Repeat(" ", indent))
buf.WriteString(fmt.Sprintf("%s = {\n", name))
for _, key := range keys {
for _, key := range slices.Sorted(maps.Keys(vals)) {
buf.WriteString(strings.Repeat(" ", indent+2))
buf.WriteString(fmt.Sprintf("%s = {", hclEscapeString(key)))
@ -513,12 +482,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
}
vals := stateVal.AsValueMap()
keys := make([]string, 0, len(vals))
for key := range vals {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
for _, key := range slices.Sorted(maps.Keys(vals)) {
buf.WriteString(strings.Repeat(" ", indent))
buf.WriteString(fmt.Sprintf("%s %q {", name, key))
// This entire map element is marked

View file

@ -15,6 +15,8 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"maps"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/instances"
@ -442,10 +444,10 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl
// traversal, but we also expose them under "resource" as an escaping
// technique if we add a reserved name in a future language edition which
// conflicts with someone's existing provider.
for k, v := range buildResourceObjects(managedResources) {
vals[k] = v
}
vals["resource"] = cty.ObjectVal(buildResourceObjects(managedResources))
builtManagedResources := buildResourceObjects(managedResources)
maps.Copy(vals, builtManagedResources)
vals["resource"] = cty.ObjectVal(builtManagedResources)
vals["ephemeral"] = cty.ObjectVal(buildResourceObjects(ephemeralResources))
vals["data"] = cty.ObjectVal(buildResourceObjects(dataResources))
vals["module"] = cty.ObjectVal(wholeModules)

View file

@ -4,6 +4,7 @@
package moduleref
import (
"maps"
"strings"
"github.com/hashicorp/go-version"
@ -23,10 +24,7 @@ type Resolver struct {
func NewResolver(internalManifest modsdir.Manifest) *Resolver {
// Since maps are pointers, create a copy of the internal manifest to
// prevent introducing side effects to the original
internalManifestCopy := make(modsdir.Manifest, len(internalManifest))
for k, v := range internalManifest {
internalManifestCopy[k] = v
}
internalManifestCopy := maps.Clone(internalManifest)
// Remove the root module entry from the internal manifest as it is
// never directly referenced.

View file

@ -14,6 +14,8 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"maps"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/configs"
@ -313,9 +315,7 @@ func (ec *EvalContext) GetOutputs() map[addrs.Run]cty.Value {
ec.outputsLock.Lock()
defer ec.outputsLock.Unlock()
outputCopy := make(map[addrs.Run]cty.Value, len(ec.runOutputs))
for k, v := range ec.runOutputs {
outputCopy[k] = v
}
maps.Copy(outputCopy, ec.runOutputs) // don't use clone here, so we can return a non-nil map
return outputCopy
}

View file

@ -8,8 +8,9 @@ import (
"encoding/json"
"fmt"
"io"
"maps"
"path"
"sort"
"slices"
"strings"
"time"
@ -158,15 +159,10 @@ func writeConfigSnapshot(snap *configload.Snapshot, z *zip.Writer) error {
// need to be user-actionable.
var manifest configSnapshotModuleManifest
keys := make([]string, 0, len(snap.Modules))
for k := range snap.Modules {
keys = append(keys, k)
}
sort.Strings(keys)
// We'll re-use this fileheader for each Create we do below.
for _, k := range keys {
for _, k := range slices.Sorted(maps.Keys(snap.Modules)) {
snapMod := snap.Modules[k]
record := configSnapshotModuleRecord{
Dir: snapMod.Dir,

View file

@ -6,6 +6,7 @@ package planfile
import (
"fmt"
"io"
"slices"
"time"
"github.com/zclconf/go-cty/cty"
@ -598,10 +599,7 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error {
rawPlan.Variables[name] = valueToTfplan(val)
}
if plan.ApplyTimeVariables.Len() != 0 {
rawPlan.ApplyTimeVariables = make([]string, 0, plan.ApplyTimeVariables.Len())
for name := range plan.ApplyTimeVariables.All() {
rawPlan.ApplyTimeVariables = append(rawPlan.ApplyTimeVariables, name)
}
rawPlan.ApplyTimeVariables = slices.Collect(plan.ApplyTimeVariables.All())
}
for _, hash := range plan.ProviderFunctionResults {

View file

@ -4,8 +4,10 @@
package states
import (
"maps"
"slices"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/zclconf/go-cty/cty"
)
// Taking deep copies of states is an important operation because state is
@ -128,45 +130,15 @@ func (os *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
return nil
}
var attrsFlat map[string]string
if os.AttrsFlat != nil {
attrsFlat = make(map[string]string, len(os.AttrsFlat))
for k, v := range os.AttrsFlat {
attrsFlat[k] = v
}
}
var attrsJSON []byte
if os.AttrsJSON != nil {
attrsJSON = make([]byte, len(os.AttrsJSON))
copy(attrsJSON, os.AttrsJSON)
}
var identityJSON []byte
if os.IdentityJSON != nil {
identityJSON = make([]byte, len(os.IdentityJSON))
copy(identityJSON, os.IdentityJSON)
}
var sensitiveAttrPaths []cty.Path
if os.AttrSensitivePaths != nil {
sensitiveAttrPaths = make([]cty.Path, len(os.AttrSensitivePaths))
copy(sensitiveAttrPaths, os.AttrSensitivePaths)
}
var private []byte
if os.Private != nil {
private = make([]byte, len(os.Private))
copy(private, os.Private)
}
attrsFlat := maps.Clone(os.AttrsFlat)
attrsJSON := slices.Clone(os.AttrsJSON)
identityJSON := slices.Clone(os.IdentityJSON)
sensitiveAttrPaths := slices.Clone(os.AttrSensitivePaths)
private := slices.Clone(os.Private)
// Some addrs.Referencable implementations are technically mutable, but
// we treat them as immutable by convention and so we don't deep-copy here.
var dependencies []addrs.ConfigResource
if os.Dependencies != nil {
dependencies = make([]addrs.ConfigResource, len(os.Dependencies))
copy(dependencies, os.Dependencies)
}
dependencies := slices.Clone(os.Dependencies)
return &ResourceInstanceObjectSrc{
Status: os.Status,
@ -197,19 +169,11 @@ func (o *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject {
return nil
}
var private []byte
if o.Private != nil {
private = make([]byte, len(o.Private))
copy(private, o.Private)
}
private := slices.Clone(o.Private)
// Some addrs.Referenceable implementations are technically mutable, but
// we treat them as immutable by convention and so we don't deep-copy here.
var dependencies []addrs.ConfigResource
if o.Dependencies != nil {
dependencies = make([]addrs.ConfigResource, len(o.Dependencies))
copy(dependencies, o.Dependencies)
}
dependencies := slices.Clone(o.Dependencies)
return &ResourceInstanceObject{
Value: o.Value,

View file

@ -13,6 +13,8 @@ import (
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
"maps"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/states"
@ -315,13 +317,7 @@ func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2,
}
}
var attributes map[string]string
if isOld.Attributes != nil {
attributes = make(map[string]string, len(isOld.Attributes))
for k, v := range isOld.Attributes {
attributes[k] = v
}
}
attributes := maps.Clone(isOld.Attributes)
if isOld.ID != "" {
// As a special case, if we don't already have an "id" attribute and
// yet there's a non-empty first-class ID on the old object then we'll

View file

@ -76,7 +76,7 @@ func (t *ephemeralResourceCloseTransformer) Transform(g *Graph) error {
return len(up) == 0
})
for _, last := range lastReferences.List() {
for last := range lastReferences.List() {
g.Connect(dag.BasicEdge(closeNode, last))
}
}