+
Skip to content

Hooks unexpectedly invoked multiple times when struct embedding is used #536

Open
@maxb

Description

@maxb

Consider this (contrived simplified) example:

package main

import (
	"fmt"

	"github.com/alecthomas/kong"
)

type Cli struct {
	CommonOptions
}

type CommonOptions struct {
	VerboseOption
}

type VerboseOption struct {
	Verbose bool
}

func (v VerboseOption) AfterApply() error {
	fmt.Printf("Verbose is set to %v\n", v.Verbose)
	return nil
}

func main() {
	kong.Parse(&Cli{})
}

Upon execution, the output is:

Verbose is set to false
Verbose is set to false
Verbose is set to false

The hook has fired three times, unexpectedly. The issue is that the code at

kong/callbacks.go

Lines 149 to 170 in 9bc3bf9

// 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)
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)...)
}
}
recurses through embedded structs, but also, a method on an embedded struct is also part of the method set of the struct it is embedded into. Effectively, Kong ends up invoking:

	(&Cli{}).AfterApply()
	(&Cli{}).CommonOptions.AfterApply()
	(&Cli{}).CommonOptions.VerboseOption.AfterApply()

This promotion into the embedding struct's method set only applies if there is no name conflict... so if the above is modified to add, e.g. a DebugOption which also has an AfterApply hook defined, as a sibling to VerboseOption, then the problematic behaviour is suppressed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载