Repository: alecthomas/kong (go)

Issue:
Kong allows multiple sub-commands to have the same name
If I specify name explicitly for sub-command structs, I can use the same one and at least `--help` does not complain but happily print out a list of sub-commands with same name. I think this should fail for ease of debugging.

Pull request description:
Panic on duplicate command names
Closes #300 

Adds supports for validating if command names are not duplicated within a single node's children.
Panics if they are duplicated.

This is a breaking change in case a project has duplicated command names.
Positional arguments are not affected and can have duplicated names.

Patch:
diff --git a/build.go b/build.go
index 4be5f24..32c83e1 100644
--- a/build.go
+++ b/build.go
@@ -158,6 +158,11 @@ MAIN:
 		}
 	}
 
+	// Validate if there are no duplicate names
+	if err := checkDuplicateNames(node, v); err != nil {
+		return nil, err
+	}
+
 	// "Unsee" flags.
 	for _, flag := range node.Flags {
 		delete(seenFlags, "--"+flag.Name)
@@ -311,3 +316,20 @@ func buildGroupForKey(k *Kong, key string) *Group {
 		Title: key,
 	}
 }
+
+func checkDuplicateNames(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 = "<anonymous struct>"
+			}
+			return fmt.Errorf("duplicate command name %q in command %q", node.Name, name)
+		}
+
+		seenNames[node.Name] = struct{}{}
+	}
+
+	return nil
+}


Test patch:
diff --git a/kong_test.go b/kong_test.go
index 8dff864..d66cbd6 100644
--- a/kong_test.go
+++ b/kong_test.go
@@ -1662,3 +1662,35 @@ func TestMapDecoderHelpfulErrorMsg(t *testing.T) {
 		})
 	}
 }
+
+func TestDuplicateName(t *testing.T) {
+	var cli struct {
+		DupA struct{} `cmd:"" name:"duplicate"`
+		DupB struct{} `cmd:"" name:"duplicate"`
+	}
+	_, err := kong.New(&cli)
+	assert.Error(t, err)
+}
+
+func TestDuplicateChildName(t *testing.T) {
+	var cli struct {
+		A struct {
+			DupA struct{} `cmd:"" name:"duplicate"`
+			DupB struct{} `cmd:"" name:"duplicate"`
+		} `cmd:""`
+		B struct{} `cmd:""`
+	}
+	_, err := kong.New(&cli)
+	assert.Error(t, err)
+}
+
+func TestChildNameCanBeDuplicated(t *testing.T) {
+	var cli struct {
+		A struct {
+			A struct{} `cmd:"" name:"duplicateA"`
+			B struct{} `cmd:"" name:"duplicateB"`
+		} `cmd:"" name:"duplicateA"`
+		B struct{} `cmd:"" name:"duplicateB"`
+	}
+	mustNew(t, &cli)
+}