x/tools/go/analysis/passes/modernize: mapsloop should use `maps.Clone` when it can prove the operand is non-nil
Open
Nobody has claimed this yet.
FeatureRequest
gopls
Tools
- Dominant language
- Go
- Stars
- 139k
- Forks
- 19.9k
- PR merge metrics
- PR metrics pending
Description
gopls version
binary compiled by latest code
go env
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='/Users/mac/go/bin'
GOCACHE='/Users/mac/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/mac/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/tq/m955dwkd1519phkp2hwpv9_80000gn/T/go-build2400307511=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/Users/mac/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/mac/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/mac/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.3'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
For the following code:
package main
import (
"fmt"
"maps"
)
var fruitProperties = map[string][]string{
"apple": {"red", "sweet", "crispy"},
"banana": {"yellow", "soft", "tropical"},
"orange": {"orange", "citrus", "juicy"},
"grape": {"purple", "small", "sweet"},
}
func GetFruitPropertiesOld() map[string][]string {
dupProperties := make(map[string][]string)
for k, v := range fruitProperties {
dupProperties[k] = v
}
return dupProperties
}
func GetFruitPropertiesNew() map[string][]string {
return maps.Clone(fruitProperties)
}
func main() {
oldCopy := GetFruitPropertiesOld()
fmt.Println(oldCopy)
newCopy := GetFruitPropertiesNew()
fmt.Println(newCopy)
newCopy["apple"] = []string{"green", "sour"}
fmt.Println("original map:", fruitProperties["apple"])
fmt.Println("new clone map:", newCopy["apple"])
}
What did you see happen?
After being processed by the modernize analyzer, it will be transformed into the following
package main
import (
"fmt"
"maps"
)
var fruitProperties = map[string][]string{
"apple": {"red", "sweet", "crispy"},
"banana": {"yellow", "soft", "tropical"},
"orange": {"orange", "citrus", "juicy"},
"grape": {"purple", "small", "sweet"},
}
func GetFruitPropertiesOld() map[string][]string {
dupProperties := make(map[string][]string)
maps.Copy(dupProperties, fruitProperties)
return dupProperties
}
func GetFruitPropertiesNew() map[string][]string {
return maps.Clone(fruitProperties)
}
func main() {
oldCopy := GetFruitPropertiesOld()
fmt.Println(oldCopy)
newCopy := GetFruitPropertiesNew()
fmt.Println(newCopy)
newCopy["apple"] = []string{"green", "sour"}
fmt.Println("original map:", fruitProperties["apple"])
fmt.Println("new clone map:", newCopy["apple"])
}
What did you expect to see?
It is better to use maps.Clone than maps.Copy here. Just like GetFruitPropertiesNew
Editor and settings
No response
Logs
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in x/tools/go/analysis/passes/modernize, focusing on the mapsloop analyzer and its existing tests. Compare the reported maps.Copy transformation with the non-nil operand example, then add coverage showing that eligible loops are transformed to maps.Clone while other cases retain their current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100