Skip to content

Commit

Permalink
refactor: minor simplification of getMethods
Browse files Browse the repository at this point in the history
  • Loading branch information
alecthomas committed Jan 30, 2025
1 parent 4be6ae6 commit 5765c11
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 44 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ linters:
- copyloopvar
- intrange
- execinquery
- nakedret

linters-settings:
govet:
Expand Down
75 changes: 31 additions & 44 deletions callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,57 +81,44 @@ func getMethod(value reflect.Value, name string) reflect.Value {
// and any embedded fields.
//
// Returns a slice of bound methods that can be called directly.
func getMethods(value reflect.Value, name string) []reflect.Value {
// Traverses embedded fields of the struct
// starting from the given value to collect all possible receivers
// for the given method name.
var traverse func(value reflect.Value, receivers []reflect.Value) []reflect.Value
traverse = func(value reflect.Value, receivers []reflect.Value) []reflect.Value {
// Always consider the current value for hooks.
receivers = append(receivers, value)

if value.Kind() == reflect.Ptr {
value = value.Elem()
}
func getMethods(value reflect.Value, name string) (methods []reflect.Value) {
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if !value.IsValid() {
return
}

// 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)
}
}
}
if method := getMethod(value, name); method.IsValid() {
methods = append(methods, method)
}

return receivers
if value.Kind() != reflect.Struct {
return
}
// 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:""`
t := value.Type()
for i := 0; i < value.NumField(); i++ {
fieldValue := value.Field(i)
field := t.Field(i)

receivers := traverse(value, nil /* receivers */)
if !field.IsExported() {
continue
}

// Search all receivers for methods
var methods []reflect.Value
for _, receiver := range receivers {
if method := getMethod(receiver, name); method.IsValid() {
methods = append(methods, method)
// 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 {
Expand Down

0 comments on commit 5765c11

Please sign in to comment.