Repository: alecthomas/kong (go)

Issue:
Support aliases for all switches
Currently it seams the `aliases` tag only works for "commands", would it be possible to enable it for normal flags as well?

```go
type Arg struct {
    TestFlag string `kong:"aliases='alt-flag'"
}
```

That would let you either run `./cmd --test-flag blah` or `./cmd --alt-flag blah` and in both cases the `TestFlag` field would contain `blah`.  Maybe this is already allowed and I'm just doing it wrong but in testing it says the aliases names are an "unknown flag".

Pull request description:
Support aliases for flags
Fixes #303

Aliases are currently only supported for sub-commands, but they're useful for flags as well. E.g., when migrating from an old flag name to a new flag name, while still supporting the old value.

Patch:
diff --git a/README.md b/README.md
index ba8275b..22e00f0 100644
--- a/README.md
+++ b/README.md
@@ -566,7 +566,7 @@ Both can coexist with standard Tag parsing.
 | `default:"1"`        | On a command, make it the default.                                                                                                                                                                                                                                                                                             |
 | `default:"withargs"` | On a command, make it the default and allow args/flags from that command                                                                                                                                                                                                                                                       |
 | `short:"X"`          | Short name, if flag.                                                                                                                                                                                                                                                                                                           |
-| `aliases:"X,Y"`      | One or more aliases (for cmd).                                                                                                                                                                                                                                                                                                 |
+| `aliases:"X,Y"`      | One or more aliases (for cmd or flag).                                                                                                                                                                                                                                                                                                 |
 | `required:""`        | If present, flag/arg is required.                                                                                                                                                                                                                                                                                              |
 | `optional:""`        | If present, flag/arg is optional.                                                                                                                                                                                                                                                                                              |
 | `hidden:""`          | If present, command or flag is hidden.                                                                                                                                                                                                                                                                                         |
diff --git a/build.go b/build.go
index e23c115..a52d90f 100644
--- a/build.go
+++ b/build.go
@@ -296,6 +296,13 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
 			return failField(v, ft, "duplicate flag --%s", value.Name)
 		}
 		seenFlags["--"+value.Name] = true
+		for _, alias := range tag.Aliases {
+			aliasFlag := "--" + alias
+			if seenFlags[aliasFlag] {
+				return failField(v, ft, "duplicate flag %s", aliasFlag)
+			}
+			seenFlags[aliasFlag] = true
+		}
 		if tag.Short != 0 {
 			if seenFlags["-"+string(tag.Short)] {
 				return failField(v, ft, "duplicate short flag -%c", tag.Short)
@@ -304,6 +311,7 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
 		}
 		flag := &Flag{
 			Value:       value,
+			Aliases:     tag.Aliases,
 			Short:       tag.Short,
 			PlaceHolder: tag.PlaceHolder,
 			Envs:        tag.Envs,
diff --git a/context.go b/context.go
index 4350608..b2bfea6 100644
--- a/context.go
+++ b/context.go
@@ -684,15 +684,24 @@ func flipBoolValue(value reflect.Value) error {
 
 func (c *Context) parseFlag(flags []*Flag, match string) (err error) {
 	candidates := []string{}
+
 	for _, flag := range flags {
 		long := "--" + flag.Name
-		short := "-" + string(flag.Short)
-		neg := "--no-" + flag.Name
+		matched := long == match
 		candidates = append(candidates, long)
 		if flag.Short != 0 {
+			short := "-" + string(flag.Short)
+			matched = matched || (short == match)
 			candidates = append(candidates, short)
 		}
-		if short != match && long != match && !(match == neg && flag.Tag.Negatable) {
+		for _, alias := range flag.Aliases {
+			alias = "--" + alias
+			matched = matched || (alias == match)
+			candidates = append(candidates, alias)
+		}
+
+		neg := "--no-" + flag.Name
+		if !matched && !(match == neg && flag.Tag.Negatable) {
 			continue
 		}
 		// Found a matching flag.
diff --git a/model.go b/model.go
index eeb547c..4e59fea 100644
--- a/model.go
+++ b/model.go
@@ -397,6 +397,7 @@ type Flag struct {
 	Xor         []string
 	PlaceHolder string
 	Envs        []string
+	Aliases     []string
 	Short       rune
 	Hidden      bool
 	Negated     bool


Test patch:
diff --git a/kong_test.go b/kong_test.go
index 650cf2a..3e71979 100644
--- a/kong_test.go
+++ b/kong_test.go
@@ -518,6 +518,16 @@ func TestShort(t *testing.T) {
 	assert.Equal(t, "hello", cli.String)
 }
 
+func TestAlias(t *testing.T) {
+	var cli struct {
+		String string `aliases:"str"`
+	}
+	app := mustNew(t, &cli)
+	_, err := app.Parse([]string{"--str", "hello"})
+	assert.NoError(t, err)
+	assert.Equal(t, "hello", cli.String)
+}
+
 func TestDuplicateFlagChoosesLast(t *testing.T) {
 	var cli struct {
 		Flag int
@@ -1321,6 +1331,24 @@ func TestDuplicateShortflags(t *testing.T) {
 	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate short flag -t")
 }
 
+func TestDuplicateAliases(t *testing.T) {
+	cli1 := struct {
+		Flag1 string `aliases:"flag"`
+		Flag2 string `aliases:"flag"`
+	}{}
+	_, err := kong.New(&cli1)
+	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate flag --flag")
+}
+
+func TestDuplicateAliasLong(t *testing.T) {
+	cli2 := struct {
+		Flag  string ``
+		Flag2 string `aliases:"flag"` // duplicates Flag
+	}{}
+	_, err := kong.New(&cli2)
+	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate flag --flag")
+}
+
 func TestDuplicateNestedShortFlags(t *testing.T) {
 	cli := struct {
 		Flag1 bool `short:"t"`