Skip to content

Commit 4fc00e4

Browse files
authored
Use reflect.TypeFor instead of reflect.TypeOf((*T)(nil)).Elem() (#4949)
## Summary - Replace the verbose `reflect.TypeOf((*T)(nil)).Elem()` idiom with `reflect.TypeFor[T]()`, available since [Go 1.22](https://go.dev/doc/go1.22#reflect) - Delete the now-redundant `calladapt.TypeOf` helper - Add a ruleguard lint rule to prevent reintroduction ## Test plan - [x] `go build ./...` - [x] `go test` for all modified packages - [x] `golangci-lint run ./...` passes - [x] Verified lint rule fires on the old pattern This pull request was AI-assisted by Isaac.
1 parent 95b7cb9 commit 4fc00e4

7 files changed

Lines changed: 54 additions & 51 deletions

File tree

bundle/direct/dresources/adapter.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func loadKeyedSlices(call *calladapt.BoundCaller) (map[string]any, error) {
154154
}
155155

156156
func (a *Adapter) initMethods(resource any) error {
157-
err := calladapt.EnsureNoExtraMethods(resource, calladapt.TypeOf[IResource]())
157+
err := calladapt.EnsureNoExtraMethods(resource, reflect.TypeFor[IResource]())
158158
if err != nil {
159159
return err
160160
}
@@ -164,7 +164,7 @@ func (a *Adapter) initMethods(resource any) error {
164164
}
165165

166166
// RemapState is optional when remote type already matches state type.
167-
a.remapState, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "RemapState")
167+
a.remapState, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "RemapState")
168168
if err != nil {
169169
return err
170170
}
@@ -186,37 +186,37 @@ func (a *Adapter) initMethods(resource any) error {
186186

187187
// Optional methods with varying signatures:
188188

189-
a.doUpdate, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "DoUpdate")
189+
a.doUpdate, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "DoUpdate")
190190
if err != nil {
191191
return err
192192
}
193193

194-
a.doUpdateWithID, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "DoUpdateWithID")
194+
a.doUpdateWithID, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "DoUpdateWithID")
195195
if err != nil {
196196
return err
197197
}
198198

199-
a.waitAfterCreate, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "WaitAfterCreate")
199+
a.waitAfterCreate, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "WaitAfterCreate")
200200
if err != nil {
201201
return err
202202
}
203203

204-
a.waitAfterUpdate, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "WaitAfterUpdate")
204+
a.waitAfterUpdate, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "WaitAfterUpdate")
205205
if err != nil {
206206
return err
207207
}
208208

209-
a.overrideChangeDesc, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "OverrideChangeDesc")
209+
a.overrideChangeDesc, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "OverrideChangeDesc")
210210
if err != nil {
211211
return err
212212
}
213213

214-
a.doResize, err = calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "DoResize")
214+
a.doResize, err = calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "DoResize")
215215
if err != nil {
216216
return err
217217
}
218218

219-
keyedSlicesCall, err := calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), "KeyedSlices")
219+
keyedSlicesCall, err := calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), "KeyedSlices")
220220
if err != nil {
221221
return err
222222
}
@@ -535,7 +535,7 @@ func (a *Adapter) KeyedSlices() map[string]any {
535535

536536
// prepareCallRequired prepares a call and ensures the method is found.
537537
func prepareCallRequired(resource any, methodName string) (*calladapt.BoundCaller, error) {
538-
caller, err := calladapt.PrepareCall(resource, calladapt.TypeOf[IResource](), methodName)
538+
caller, err := calladapt.PrepareCall(resource, reflect.TypeFor[IResource](), methodName)
539539
if err != nil {
540540
return nil, fmt.Errorf("%s: %w", methodName, err)
541541
}

libs/calladapt/calladapt.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ import (
55
"reflect"
66
)
77

8-
// TypeOf returns reflect.Type for type parameter T, analogous to
9-
// reflect.TypeOf((*T)(nil)).Elem().
10-
func TypeOf[T any]() reflect.Type {
11-
var t *T
12-
return reflect.TypeOf(t).Elem()
13-
}
14-
158
// BoundCaller encapsulates a bound method and metadata about its signature.
169
// It can invoke the underlying function and returns all non-error outputs and
1710
// the error (if the method returns one as the last return value).
@@ -102,8 +95,8 @@ func (c *BoundCaller) Call(args ...any) ([]any, error) {
10295
}
10396

10497
var (
105-
errType = TypeOf[error]()
106-
anyType = TypeOf[any]()
98+
errType = reflect.TypeFor[error]()
99+
anyType = reflect.TypeFor[any]()
107100
)
108101

109102
// PrepareCall creates a unified BoundCaller for the given method on receiver that matches the ifaceType method.

libs/calladapt/calladapt_test.go

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -118,47 +118,47 @@ func TestPrepareCallErrors(t *testing.T) {
118118
{
119119
name: "void method is supported",
120120
recv: (*MyStruct)(nil),
121-
ifaceType: TypeOf[interface{ PMethodVoid() }](),
121+
ifaceType: reflect.TypeFor[interface{ PMethodVoid() }](),
122122
method: "PMethodVoid",
123123
},
124124
{
125125
name: "correct number of args - concrete matching argument type",
126126
recv: (*MyStruct)(nil),
127-
ifaceType: TypeOf[interface{ PMethodAcceptData(data Data) error }](),
127+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data Data) error }](),
128128
method: "PMethodAcceptData",
129129
},
130130
{
131131
name: "correct number of args - interface argument is any",
132132
recv: (*MyStruct)(nil),
133-
ifaceType: TypeOf[interface{ PMethodAcceptData(data any) error }](),
133+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data any) error }](),
134134
method: "PMethodAcceptData",
135135
},
136136
{
137137
name: "correct number of args - concrete mismatching argument type",
138138
recv: (*MyStruct)(nil),
139-
ifaceType: TypeOf[interface{ PMethodAcceptData(data NewData) error }](),
139+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data NewData) error }](),
140140
method: "PMethodAcceptData",
141141
errMsg: "interface { PMethodAcceptData(calladapt.NewData) error }.PMethodAcceptData: param 0 mismatch: interface calladapt.NewData, concrete calladapt.Data",
142142
},
143143
{
144144
name: "incorrect number of args",
145145
recv: (*MyStruct)(nil),
146-
ifaceType: TypeOf[interface{ PMethodAcceptData() error }](),
146+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData() error }](),
147147
method: "PMethodAcceptData",
148148
errMsg: "interface { PMethodAcceptData() error }.PMethodAcceptData: param count mismatch: interface 0, concrete 1",
149149
},
150150
{
151151
name: "incorrect number of return values",
152152
recv: (*MyStruct)(nil),
153-
ifaceType: TypeOf[interface{ PMethodAcceptData(any) (any, error) }](),
153+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(any) (any, error) }](),
154154
method: "PMethodAcceptData",
155155
errMsg: "interface { PMethodAcceptData(interface {}) (interface {}, error) }.PMethodAcceptData: return count mismatch: interface 2, concrete 1",
156156
unexpected: true,
157157
},
158158
{
159159
name: "error return convertible to any",
160160
recv: (*MyStruct)(nil),
161-
ifaceType: TypeOf[interface{ PMethodAcceptData(any) any }](),
161+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(any) any }](),
162162
method: "PMethodAcceptData",
163163
},
164164
{
@@ -171,42 +171,42 @@ func TestPrepareCallErrors(t *testing.T) {
171171
{
172172
name: "untyped nil receiver",
173173
recv: nil,
174-
ifaceType: TypeOf[interface{ PMethodAcceptData(any) (any, error) }](),
174+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(any) (any, error) }](),
175175
method: "PMethodAcceptData",
176176
errMsg: "first argument must not be untyped nil",
177177
},
178178
{
179179
name: "method is not on interface",
180180
recv: (*MyStruct)(nil),
181-
ifaceType: TypeOf[any](),
181+
ifaceType: reflect.TypeFor[any](),
182182
method: "PMethodAcceptData",
183183
errMsg: "interface {} has no method \"PMethodAcceptData\"",
184184
},
185185
{
186186
name: "method is not on receiver",
187187
recv: (*MyStruct)(nil),
188-
ifaceType: TypeOf[interface{ Hello(any) (any, error) }](),
188+
ifaceType: reflect.TypeFor[interface{ Hello(any) (any, error) }](),
189189
method: "Hello",
190190
methodNotFound: true,
191191
},
192192
{
193193
name: "any instead of error allowed",
194194
recv: (*MyStruct)(nil),
195-
ifaceType: TypeOf[interface{ PMethodAcceptData(data Data) any }](),
195+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data Data) any }](),
196196
method: "PMethodAcceptData",
197197
},
198198
{
199199
name: "error type mismatch",
200200
recv: (*MyStruct)(nil),
201-
ifaceType: TypeOf[interface{ GetCustomError() error }](),
201+
ifaceType: reflect.TypeFor[interface{ GetCustomError() error }](),
202202
method: "GetCustomError",
203203
errMsg: "interface { GetCustomError() error }.GetCustomError: result 0 mismatch: interface error, concrete calladapt.CustomError",
204204
unexpected: true,
205205
},
206206
{
207207
name: "two returns without error are supported",
208208
recv: (*MyStruct)(nil),
209-
ifaceType: TypeOf[interface{ BadMethod() (int, string) }](),
209+
ifaceType: reflect.TypeFor[interface{ BadMethod() (int, string) }](),
210210
method: "BadMethod",
211211
},
212212
}
@@ -248,111 +248,111 @@ func TestCall(t *testing.T) {
248248
{
249249
name: "nil receiver - PMethodAcceptData ok",
250250
recv: (*MyStruct)(nil),
251-
ifaceType: TypeOf[interface{ PMethodAcceptData(data Data) error }](),
251+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data Data) error }](),
252252
method: "PMethodAcceptData",
253253
args: []any{Data{}},
254254
expect: []any{},
255255
},
256256
{
257257
name: "error return",
258258
recv: (*MyStruct)(nil),
259-
ifaceType: TypeOf[interface{ PMethodAcceptData(data Data) error }](),
259+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptData(data Data) error }](),
260260
method: "PMethodAcceptData",
261261
args: []any{Data{1}},
262262
errMsg: "X cannot be 1",
263263
},
264264
{
265265
name: "value return",
266266
recv: my,
267-
ifaceType: TypeOf[interface{ VMethodTransformNoError(any) any }](),
267+
ifaceType: reflect.TypeFor[interface{ VMethodTransformNoError(any) any }](),
268268
method: "VMethodTransformNoError",
269269
args: []any{Data{2}},
270270
expect: []any{NewData{Y: 12}},
271271
},
272272
{
273273
name: "value return with ptr args",
274274
recv: &my,
275-
ifaceType: TypeOf[interface{ PMethodTransformPtrNoError(any) any }](),
275+
ifaceType: reflect.TypeFor[interface{ PMethodTransformPtrNoError(any) any }](),
276276
method: "PMethodTransformPtrNoError",
277277
args: []any{&Data{2}},
278278
expect: []any{&NewData{Y: 12}},
279279
},
280280
{
281281
name: "any+error return",
282282
recv: &my,
283-
ifaceType: TypeOf[interface{ PMethodTransformData(data Data) (any, error) }](),
283+
ifaceType: reflect.TypeFor[interface{ PMethodTransformData(data Data) (any, error) }](),
284284
method: "PMethodTransformData",
285285
args: []any{Data{2}},
286286
expect: []any{NewData{Y: 22}},
287287
},
288288
{
289289
name: "any+error return, error case",
290290
recv: &MyStruct{State: 0},
291-
ifaceType: TypeOf[interface{ PMethodTransformData(data Data) (any, error) }](),
291+
ifaceType: reflect.TypeFor[interface{ PMethodTransformData(data Data) (any, error) }](),
292292
method: "PMethodTransformData",
293293
args: []any{Data{1}},
294294
errMsg: "X cannot be 1",
295295
},
296296
{
297297
name: "ptr any+error return",
298298
recv: &MyStruct{State: 0},
299-
ifaceType: TypeOf[interface{ PMethodTransformDataPtr(data *Data) (any, error) }](),
299+
ifaceType: reflect.TypeFor[interface{ PMethodTransformDataPtr(data *Data) (any, error) }](),
300300
method: "PMethodTransformDataPtr",
301301
args: []any{&Data{2}},
302302
expect: []any{&NewData{Y: 12}},
303303
},
304304
{
305305
name: "ptr any+error return, error case (nil)",
306306
recv: &MyStruct{State: 0},
307-
ifaceType: TypeOf[interface{ PMethodTransformDataPtr(data *Data) (any, error) }](),
307+
ifaceType: reflect.TypeFor[interface{ PMethodTransformDataPtr(data *Data) (any, error) }](),
308308
method: "PMethodTransformDataPtr",
309309
args: []any{nil},
310310
errMsg: "data is nil",
311311
},
312312
{
313313
name: "void method call returns no outs",
314314
recv: &my,
315-
ifaceType: TypeOf[interface{ PMethodVoid() }](),
315+
ifaceType: reflect.TypeFor[interface{ PMethodVoid() }](),
316316
method: "PMethodVoid",
317317
args: []any{},
318318
expect: []any{},
319319
},
320320
{
321321
name: "too many args error",
322322
recv: my,
323-
ifaceType: TypeOf[interface{ VMethodTransformNoError(data Data) any }](),
323+
ifaceType: reflect.TypeFor[interface{ VMethodTransformNoError(data Data) any }](),
324324
method: "VMethodTransformNoError",
325325
args: []any{Data{1}, Data{2}},
326326
errMsg: "VMethodTransformNoError: want 1 args, got 2",
327327
},
328328
{
329329
name: "wrong arg type error (different pointer)",
330330
recv: &my,
331-
ifaceType: TypeOf[interface{ PMethodTransformPtrNoError(data *Data) any }](),
331+
ifaceType: reflect.TypeFor[interface{ PMethodTransformPtrNoError(data *Data) any }](),
332332
method: "PMethodTransformPtrNoError",
333333
args: []any{&NewData{}},
334334
errMsg: "PMethodTransformPtrNoError: arg 0 type mismatch: want *calladapt.Data, got *calladapt.NewData",
335335
},
336336
{
337337
name: "nil interface param allowed",
338338
recv: &my,
339-
ifaceType: TypeOf[interface{ PMethodAcceptAny(v any) error }](),
339+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptAny(v any) error }](),
340340
method: "PMethodAcceptAny",
341341
args: []any{nil},
342342
errMsg: "PMethodAcceptAny: arg 0 type mismatch: want interface {}, got nil",
343343
},
344344
{
345345
name: "nil slice param allowed",
346346
recv: &my,
347-
ifaceType: TypeOf[interface{ PMethodAcceptSlice(s []int) int }](),
347+
ifaceType: reflect.TypeFor[interface{ PMethodAcceptSlice(s []int) int }](),
348348
method: "PMethodAcceptSlice",
349349
args: []any{nil},
350350
errMsg: "PMethodAcceptSlice: arg 0 type mismatch: want []int, got nil",
351351
},
352352
{
353353
name: "DoCreate returns id",
354354
recv: &my,
355-
ifaceType: TypeOf[interface {
355+
ifaceType: reflect.TypeFor[interface {
356356
DoCreate(ctx context.Context, data *Data) (string, error)
357357
}](),
358358
method: "DoCreate",

libs/calladapt/validate_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package calladapt_test
22

33
import (
4+
"reflect"
45
"testing"
56

67
"github.com/databricks/cli/libs/calladapt"
@@ -32,19 +33,19 @@ func (*badType) Extra() {}
3233

3334
func TestEnsureNoExtraMethods_AllowsPartial(t *testing.T) {
3435
typedNil := (*partialType)(nil)
35-
err := calladapt.EnsureNoExtraMethods(typedNil, calladapt.TypeOf[testIface]())
36+
err := calladapt.EnsureNoExtraMethods(typedNil, reflect.TypeFor[testIface]())
3637
require.NoError(t, err)
3738
}
3839

3940
func TestEnsureNoExtraMethods_AllowsGood(t *testing.T) {
4041
typedNil := (*goodType)(nil)
41-
err := calladapt.EnsureNoExtraMethods(typedNil, calladapt.TypeOf[testIface]())
42+
err := calladapt.EnsureNoExtraMethods(typedNil, reflect.TypeFor[testIface]())
4243
require.NoError(t, err)
4344
}
4445

4546
func TestEnsureNoExtraMethods_RejectsExtra(t *testing.T) {
4647
typedNil := (*badType)(nil)
47-
err := calladapt.EnsureNoExtraMethods(typedNil, calladapt.TypeOf[testIface]())
48+
err := calladapt.EnsureNoExtraMethods(typedNil, reflect.TypeFor[testIface]())
4849
require.Error(t, err)
4950
assert.Equal(t, "unexpected method Extra on *calladapt_test.badType; only methods from [calladapt_test.testIface] are allowed", err.Error())
5051
}

libs/dyn/convert/struct_info.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func (s *structInfo) FieldValues(v reflect.Value) []FieldValue {
190190
}
191191

192192
// Type of [dyn.Value].
193-
var configValueType = reflect.TypeOf((*dyn.Value)(nil)).Elem()
193+
var configValueType = reflect.TypeFor[dyn.Value]()
194194

195195
// getForceSendFieldsValues collects ForceSendFields reflect.Values
196196
// Returns map[structKey]reflect.Value where structKey is -1 for direct fields, embedded index for embedded fields
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package gorules
2+
3+
import "github.com/quasilyte/go-ruleguard/dsl"
4+
5+
// UseReflectTypeFor detects reflect.TypeOf((*T)(nil)).Elem() and suggests reflect.TypeFor[T]() instead.
6+
func UseReflectTypeFor(m dsl.Matcher) {
7+
m.Match(`reflect.TypeOf(($x)(nil)).Elem()`).
8+
Report(`Use reflect.TypeFor instead of reflect.TypeOf((*T)(nil)).Elem()`)
9+
}

libs/utils/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
// We must use that when copying structs because JSON marshaller in SDK crashes if it sees unknown field.
1010
func FilterFields[T any](fields []string, excludeFields ...string) []string {
1111
var result []string
12-
typeOfT := reflect.TypeOf((*T)(nil)).Elem()
12+
typeOfT := reflect.TypeFor[T]()
1313

1414
excludeMap := make(map[string]bool)
1515
for _, exclude := range excludeFields {

0 commit comments

Comments
 (0)