Skip to content

Commit e5a0863

Browse files
committed
go/analysis/passes/modernize: disable reflecttypefor modernizer for unnamed struct/interface
When a type contains an unnamed struct or an unnamed interface, we should not suggest a fix because it will involve writing out the entire type and will be more verbose than the existing code. Fixes golang/go#76698 Change-Id: I5fdeb8dc5b7e70b2201fdf1fe714335aef439cc7 Reviewed-on: https://go-review.googlesource.com/c/tools/+/726941 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent e2dd416 commit e5a0863

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

go/analysis/passes/modernize/reflect.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ func reflecttypefor(pass *analysis.Pass) (any, error) {
102102
continue // e.g. reflect was dot-imported
103103
}
104104

105+
// Don't offer a fix if the type contains an unnamed struct or unnamed
106+
// interface because the replacement would be significantly more verbose.
107+
// (See golang/go#76698)
108+
if isComplicatedType(t) {
109+
continue
110+
}
111+
112+
// Don't offer the fix if the type string is too long. We define "too
113+
// long" as more than three times the length of the original expression
114+
// and at least 16 characters (a 3x length increase of a very
115+
// short expression should not be cause for skipping the fix).
116+
oldLen := int(expr.End() - expr.Pos())
117+
newLen := len(tstr)
118+
if newLen >= 16 && newLen > 3*oldLen {
119+
continue
120+
}
121+
105122
// If the call argument contains the last use
106123
// of a variable, as in:
107124
// var zero T
@@ -137,3 +154,43 @@ func reflecttypefor(pass *analysis.Pass) (any, error) {
137154

138155
return nil, nil
139156
}
157+
158+
// isComplicatedType reports whether type t is complicated, e.g. it is or contains an
159+
// unnamed struct, interface, or function signature.
160+
func isComplicatedType(t types.Type) bool {
161+
var check func(typ types.Type) bool
162+
check = func(typ types.Type) bool {
163+
switch t := typ.(type) {
164+
case typesinternal.NamedOrAlias:
165+
for ta := range t.TypeArgs().Types() {
166+
if check(ta) {
167+
return true
168+
}
169+
}
170+
return false
171+
case *types.Struct, *types.Interface, *types.Signature:
172+
// These are complex types with potentially many elements
173+
// so we should avoid duplicating their definition.
174+
return true
175+
case *types.Pointer:
176+
return check(t.Elem())
177+
case *types.Slice:
178+
return check(t.Elem())
179+
case *types.Array:
180+
return check(t.Elem())
181+
case *types.Chan:
182+
return check(t.Elem())
183+
case *types.Map:
184+
return check(t.Key()) || check(t.Elem())
185+
case *types.Basic:
186+
return false
187+
case *types.TypeParam:
188+
return false
189+
default:
190+
// Includes types.Union
191+
return true
192+
}
193+
}
194+
195+
return check(t)
196+
}

go/analysis/passes/modernize/testdata/src/reflecttypefor/reflecttypefor.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ import (
66
"time"
77
)
88

9+
type A string
10+
11+
type B[T any] int
12+
913
var (
1014
x any
15+
a A
16+
b B[int]
1117
_ = reflect.TypeOf(x) // nope (dynamic)
1218
_ = reflect.TypeOf(0) // want "reflect.TypeOf call can be simplified using TypeFor"
1319
_ = reflect.TypeOf(uint(0)) // want "reflect.TypeOf call can be simplified using TypeFor"
@@ -18,6 +24,8 @@ var (
1824
_ = reflect.TypeOf(*new(time.Time)) // want "reflect.TypeOf call can be simplified using TypeFor"
1925
_ = reflect.TypeOf(time.Time{}) // want "reflect.TypeOf call can be simplified using TypeFor"
2026
_ = reflect.TypeOf(time.Duration(0)) // want "reflect.TypeOf call can be simplified using TypeFor"
27+
_ = reflect.TypeOf(&a) // want "reflect.TypeOf call can be simplified using TypeFor"
28+
_ = reflect.TypeOf(&b) // want "reflect.TypeOf call can be simplified using TypeFor"
2129
)
2230

2331
// Eliminate local var if we deleted its last use.
@@ -29,3 +37,53 @@ func _() {
2937
_ = reflect.TypeOf(z2) // want "reflect.TypeOf call can be simplified using TypeFor"
3038
_ = z2 // z2 has multiple uses
3139
}
40+
41+
type T struct {
42+
f struct {
43+
A bool
44+
B int
45+
C string
46+
}
47+
}
48+
49+
type S struct {
50+
f [2]struct {
51+
A bool
52+
B int
53+
C string
54+
}
55+
}
56+
57+
type R []struct {
58+
A int
59+
}
60+
61+
type M[T struct{ F int }] int
62+
63+
type P struct {
64+
f interface {
65+
}
66+
g func() // fine length
67+
68+
long func(a int, b int, c int) (bool, string, int) // too long
69+
70+
s func(a struct{})
71+
72+
q func() struct{}
73+
}
74+
75+
func f(t *T, r R, m *M[struct{ F int }], s *S, p *P) {
76+
// No suggested fix for all of the following because the type is complicated -- e.g. has an unnamed struct,
77+
// interface, or signature -- so the fix would be more verbose than the original expression.
78+
// Also because structs and interfaces often acquire new fields and methods, and the type string
79+
// produced by this modernizer won't get updated automatically, potentially causing a bug.
80+
_ = reflect.TypeOf(&t.f)
81+
_ = reflect.TypeOf(r[0])
82+
_ = reflect.TypeOf(m)
83+
_ = reflect.TypeOf(&s.f)
84+
_ = reflect.TypeOf(&p.f)
85+
_ = reflect.TypeOf(&p.g)
86+
_ = reflect.TypeOf(&p.long)
87+
_ = reflect.TypeOf(&p.q)
88+
_ = reflect.TypeOf(&p.s)
89+
}

go/analysis/passes/modernize/testdata/src/reflecttypefor/reflecttypefor.go.golden

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ import (
66
"time"
77
)
88

9+
type A string
10+
11+
type B[T any] int
12+
913
var (
1014
x any
15+
a A
16+
b B[int]
1117
_ = reflect.TypeOf(x) // nope (dynamic)
1218
_ = reflect.TypeFor[int]() // want "reflect.TypeOf call can be simplified using TypeFor"
1319
_ = reflect.TypeFor[uint]() // want "reflect.TypeOf call can be simplified using TypeFor"
@@ -18,6 +24,8 @@ var (
1824
_ = reflect.TypeFor[time.Time]() // want "reflect.TypeOf call can be simplified using TypeFor"
1925
_ = reflect.TypeFor[time.Time]() // want "reflect.TypeOf call can be simplified using TypeFor"
2026
_ = reflect.TypeFor[time.Duration]() // want "reflect.TypeOf call can be simplified using TypeFor"
27+
_ = reflect.TypeFor[*A]() // want "reflect.TypeOf call can be simplified using TypeFor"
28+
_ = reflect.TypeFor[*B[int]]() // want "reflect.TypeOf call can be simplified using TypeFor"
2129
)
2230

2331
// Eliminate local var if we deleted its last use.
@@ -28,3 +36,53 @@ func _() {
2836
_ = reflect.TypeFor[string]() // want "reflect.TypeOf call can be simplified using TypeFor"
2937
_ = z2 // z2 has multiple uses
3038
}
39+
40+
type T struct {
41+
f struct {
42+
A bool
43+
B int
44+
C string
45+
}
46+
}
47+
48+
type S struct {
49+
f [2]struct {
50+
A bool
51+
B int
52+
C string
53+
}
54+
}
55+
56+
type R []struct {
57+
A int
58+
}
59+
60+
type M[T struct{ F int }] int
61+
62+
type P struct {
63+
f interface {
64+
}
65+
g func() // fine length
66+
67+
long func(a int, b int, c int) (bool, string, int) // too long
68+
69+
s func(a struct{})
70+
71+
q func() struct{}
72+
}
73+
74+
func f(t *T, r R, m *M[struct{ F int }], s *S, p *P) {
75+
// No suggested fix for all of the following because the type is complicated -- e.g. has an unnamed struct,
76+
// interface, or signature -- so the fix would be more verbose than the original expression.
77+
// Also because structs and interfaces often acquire new fields and methods, and the type string
78+
// produced by this modernizer won't get updated automatically, potentially causing a bug.
79+
_ = reflect.TypeOf(&t.f)
80+
_ = reflect.TypeOf(r[0])
81+
_ = reflect.TypeOf(m)
82+
_ = reflect.TypeOf(&s.f)
83+
_ = reflect.TypeOf(&p.f)
84+
_ = reflect.TypeOf(&p.g)
85+
_ = reflect.TypeOf(&p.long)
86+
_ = reflect.TypeOf(&p.q)
87+
_ = reflect.TypeOf(&p.s)
88+
}

0 commit comments

Comments
 (0)