diff --git a/build.go b/build.go index a52d90f..466514e 100644 --- a/build.go +++ b/build.go @@ -160,7 +160,7 @@ MAIN: } // Validate if there are no duplicate names - if err := checkDuplicateNames(node, v); err != nil { + if err := checkDuplicateNamesAndAliases(node, v); err != nil { return nil, err } @@ -342,19 +342,31 @@ func buildGroupForKey(k *Kong, key string) *Group { } } -func checkDuplicateNames(node *Node, v reflect.Value) error { +func checkDuplicateNamesAndAliases(node *Node, v reflect.Value) error { seenNames := make(map[string]struct{}) for _, node := range node.Children { if _, ok := seenNames[node.Name]; ok { - name := v.Type().Name() - if name == "" { - name = "" - } - return fmt.Errorf("duplicate command name %q in command %q", node.Name, name) + return fmt.Errorf("duplicate command name %q in command %q", node.Name, getStructName(v)) } seenNames[node.Name] = struct{}{} + + for _, alias := range node.Aliases { + if _, ok := seenNames[alias]; ok { + return fmt.Errorf("duplicate alias %q in command %q with parent %q", alias, node.Name, getStructName(v)) + } + + seenNames[alias] = struct{}{} + } } return nil } + +func getStructName(v reflect.Value) string { + name := v.Type().Name() + if name == "" { + name = "" + } + return name +} diff --git a/kong_test.go b/kong_test.go index bd1ecfa..e5789fb 100644 --- a/kong_test.go +++ b/kong_test.go @@ -1766,6 +1766,47 @@ func TestChildNameCanBeDuplicated(t *testing.T) { mustNew(t, &cli) } +func TestDuplicateAliases(t *testing.T) { + var cli struct { + DupA struct{} `cmd:"" aliases:"alias"` + DupB struct{} `cmd:"" aliases:"alias"` + } + + _, err := kong.New(&cli) + assert.Error(t, err) +} + +func TestDuplicateAliasesInCommand(t *testing.T) { + var cli struct { + DupA struct{} `cmd:"" aliases:"alias,alias"` + DupB struct{} `cmd:""` + } + _, err := kong.New(&cli) + assert.Error(t, err) +} + +func TestDuplicateChildAliases(t *testing.T) { + var cli struct { + A struct { + DupA struct{} `cmd:"" aliases:"alias"` + DupB struct{} `cmd:"" aliases:"alias"` + } `cmd:""` + B struct{} `cmd:""` + } + _, err := kong.New(&cli) + assert.Error(t, err) +} + +func TestChildAliasesCanBeDuplicated(t *testing.T) { + var cli struct { + A struct { + A struct{} `cmd:"" aliases:"alias"` + } `cmd:"" aliases:"alias"` + B struct{} `cmd:""` + } + mustNew(t, &cli) +} + func TestCumulativeArgumentLast(t *testing.T) { var cli struct { Arg1 string `arg:""` diff --git a/tag_test.go b/tag_test.go index f648edc..4c3e30d 100644 --- a/tag_test.go +++ b/tag_test.go @@ -175,20 +175,6 @@ func TestTagAliases(t *testing.T) { assert.Equal(t, "arg", cli.Cmd.Arg) } -func TestTagAliasesConflict(t *testing.T) { - type Command struct { - Arg string `arg help:"Some arg"` - } - var cli struct { - Cmd Command `cmd hidden aliases:"other-cmd"` - OtherCmd Command `cmd` - } - p := mustNew(t, &cli) - _, err := p.Parse([]string{"other-cmd", "arg"}) - assert.NoError(t, err) - assert.Equal(t, "arg", cli.OtherCmd.Arg) -} - func TestTagAliasesSub(t *testing.T) { type SubCommand struct { Arg string `arg help:"Some arg"`