[tools] go/analysis/passes/modernize: avoid breaking unkeyed struct literals

0 views
Skip to first unread message

shuang cui (Gerrit)

unread,
Aug 11, 2026, 10:35:58 PM (5 hours ago) Aug 11
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

shuang cui has uploaded the change for review

Commit message

go/analysis/passes/modernize: avoid breaking unkeyed struct literals

The atomictypes analyzer only considers explicit uses of a struct field.
Unkeyed composite literals such as S{1} contain no field identifier, so
changing an int32 field to atomic.Int32 can make the program ill-typed.

Skip the modernization of a struct field when its containing struct type
is used in an unkeyed composite literal. Add tests for named, nested, and
anonymous struct literals.

Fixes golang/go#80839
Change-Id: Ib9ff40d4c27238a972c8d5d4e2f4377693510505

Change diff

diff --git a/go/analysis/passes/modernize/atomictypes.go b/go/analysis/passes/modernize/atomictypes.go
index 6fd618f..382295f 100644
--- a/go/analysis/passes/modernize/atomictypes.go
+++ b/go/analysis/passes/modernize/atomictypes.go
@@ -64,8 +64,9 @@
}

var (
- index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)
- info = pass.TypesInfo
+ inspect = pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+ index = pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index)
+ info = pass.TypesInfo
)

// Gather all candidate variables v appearing
@@ -123,6 +124,14 @@
case *ast.Field: // struct { v int }
names = parent.Names
typ = parent.Type
+ structCursor := def.Parent().Parent().Parent()
+ structType := info.TypeOf(structCursor.Node().(*ast.StructType))
+ if typeSpec, ok := structCursor.Parent().Node().(*ast.TypeSpec); ok {
+ structType = info.Defs[typeSpec.Name].Type()
+ }
+ if hasUnkeyedLiteralOfStruct(inspect, info, structType) {
+ continue
+ }
case *ast.ValueSpec: // var v int
if len(parent.Values) > 0 {
// e.g. var v int = 5
@@ -249,3 +258,37 @@

return nil, nil
}
+
+// hasUnkeyedLiteralOfStruct reports whether the package contains an unkeyed
+// composite literal of the specified struct type. Such literals don't contain
+// identifiers for their fields, so they don't appear in the type index's uses
+// of a field object. Changing a field's type may make them ill-typed.
+func hasUnkeyedLiteralOfStruct(inspect *inspector.Inspector, info *types.Info, structType types.Type) bool {
+ for lit := range inspect.Root().Preorder((*ast.CompositeLit)(nil)) {
+ composite := lit.Node().(*ast.CompositeLit)
+ if len(composite.Elts) == 0 {
+ continue
+ }
+ if _, keyed := composite.Elts[0].(*ast.KeyValueExpr); keyed {
+ continue
+ }
+ if typ := info.TypeOf(composite); typ != nil && identicalStructType(typ, structType) {
+ return true
+ }
+ }
+ return false
+}
+
+// identicalStructType reports whether x and y denote the same struct type,
+// treating instances of the same generic named type as identical for the
+// purpose of this conservative check.
+func identicalStructType(x, y types.Type) bool {
+ x = types.Unalias(x)
+ y = types.Unalias(y)
+ if types.Identical(x, y) {
+ return true
+ }
+ xnamed, xok := x.(*types.Named)
+ ynamed, yok := y.(*types.Named)
+ return xok && yok && xnamed.Origin() == ynamed.Origin()
+}
diff --git a/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go b/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go
index f6a825e..5f92c82 100644
--- a/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go
+++ b/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go
@@ -9,6 +9,23 @@
x int32 // want "var x int32 may be simplified using atomic.Int32"
}

+type Unkeyed struct {
+ x int32
+}
+
+var _ = Unkeyed{1} // nope: can't assign an int to an atomic.Int32
+var _ = []Unkeyed{{1}} // also detect literals whose type is elided
+
+func unkeyed(s *Unkeyed) {
+ atomic.AddInt32(&s.x, 1)
+}
+
+var unkeyedAnonymous = struct{ x int32 }{1}
+
+func useUnkeyedAnonymous() {
+ atomic.AddInt32(&unkeyedAnonymous.x, 1)
+}
+
type Z struct {
y int64 // want "var y int64 may be simplified using atomic.Int64"
z int64
diff --git a/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go.golden b/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go.golden
index 7c995a7..af5d9f9 100644
--- a/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go.golden
+++ b/go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go.golden
@@ -9,6 +9,23 @@
x atomic.Int32 // want "var x int32 may be simplified using atomic.Int32"
}

+type Unkeyed struct {
+ x int32
+}
+
+var _ = Unkeyed{1} // nope: can't assign an int to an atomic.Int32
+var _ = []Unkeyed{{1}} // also detect literals whose type is elided
+
+func unkeyed(s *Unkeyed) {
+ atomic.AddInt32(&s.x, 1)
+}
+
+var unkeyedAnonymous = struct{ x int32 }{1}
+
+func useUnkeyedAnonymous() {
+ atomic.AddInt32(&unkeyedAnonymous.x, 1)
+}
+
type Z struct {
y atomic.Int64 // want "var y int64 may be simplified using atomic.Int64"
z int64
@@ -51,4 +68,4 @@
}
atomic.AddInt64(&w.z, 1) // nope - cannot fix initial value assignment
return
-}
\ No newline at end of file
+}

Change information

Files:
  • M go/analysis/passes/modernize/atomictypes.go
  • M go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go
  • M go/analysis/passes/modernize/testdata/src/atomictypes/atomic.go.golden
Change size: M
Delta: 3 files changed, 80 insertions(+), 3 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Ib9ff40d4c27238a972c8d5d4e2f4377693510505
Gerrit-Change-Number: 813720
Gerrit-PatchSet: 1
Gerrit-Owner: shuang cui <imc...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

shuang cui (Gerrit)

unread,
Aug 11, 2026, 10:36:57 PM (5 hours ago) Aug 11
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

shuang cui voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Ib9ff40d4c27238a972c8d5d4e2f4377693510505
Gerrit-Change-Number: 813720
Gerrit-PatchSet: 1
Gerrit-Owner: shuang cui <imc...@gmail.com>
Gerrit-Reviewer: shuang cui <imc...@gmail.com>
Gerrit-Comment-Date: Wed, 12 Aug 2026 02:36:49 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages