refactor: minor simplification of getMethods

This commit is contained in:
Alec Thomas
2025-01-30 14:08:07 +11:00
parent 4be6ae6168
commit 5765c11522
2 changed files with 34 additions and 46 deletions
+1
View File
@@ -42,6 +42,7 @@ linters:
- copyloopvar - copyloopvar
- intrange - intrange
- execinquery - execinquery
- nakedret
linters-settings: linters-settings:
govet: govet:
+33 -46
View File
@@ -81,57 +81,44 @@ func getMethod(value reflect.Value, name string) reflect.Value {
// and any embedded fields. // and any embedded fields.
// //
// Returns a slice of bound methods that can be called directly. // Returns a slice of bound methods that can be called directly.
func getMethods(value reflect.Value, name string) []reflect.Value { func getMethods(value reflect.Value, name string) (methods []reflect.Value) {
// Traverses embedded fields of the struct if value.Kind() == reflect.Ptr {
// starting from the given value to collect all possible receivers value = value.Elem()
// for the given method name. }
var traverse func(value reflect.Value, receivers []reflect.Value) []reflect.Value if !value.IsValid() {
traverse = func(value reflect.Value, receivers []reflect.Value) []reflect.Value { return
// Always consider the current value for hooks.
receivers = append(receivers, value)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
// If the current value is a struct, also consider embedded fields.
// Two kinds of embedded fields are considered if they're exported:
//
// - standard Go embedded fields
// - fields tagged with `embed:""`
if value.Kind() == reflect.Struct {
t := value.Type()
for i := 0; i < value.NumField(); i++ {
fieldValue := value.Field(i)
field := t.Field(i)
if !field.IsExported() {
continue
}
// Consider a field embedded if it's actually embedded
// or if it's tagged with `embed:""`.
_, isEmbedded := field.Tag.Lookup("embed")
isEmbedded = isEmbedded || field.Anonymous
if isEmbedded {
receivers = traverse(fieldValue, receivers)
}
}
}
return receivers
} }
receivers := traverse(value, nil /* receivers */) if method := getMethod(value, name); method.IsValid() {
methods = append(methods, method)
}
// Search all receivers for methods if value.Kind() != reflect.Struct {
var methods []reflect.Value return
for _, receiver := range receivers { }
if method := getMethod(receiver, name); method.IsValid() { // If the current value is a struct, also consider embedded fields.
methods = append(methods, method) // Two kinds of embedded fields are considered if they're exported:
//
// - standard Go embedded fields
// - fields tagged with `embed:""`
t := value.Type()
for i := 0; i < value.NumField(); i++ {
fieldValue := value.Field(i)
field := t.Field(i)
if !field.IsExported() {
continue
}
// Consider a field embedded if it's actually embedded
// or if it's tagged with `embed:""`.
_, isEmbedded := field.Tag.Lookup("embed")
isEmbedded = isEmbedded || field.Anonymous
if isEmbedded {
methods = append(methods, getMethods(fieldValue, name)...)
} }
} }
return methods return
} }
func callFunction(f reflect.Value, bindings bindings) error { func callFunction(f reflect.Value, bindings bindings) error {