cmd/stringer: accept simple type conversion expressions in constant ValueSpec

This permits constants of the form `const X = T(A)` to add `X` to the
stringer output for type `T`.

While those constants can be rewritten as `const X T = T(A)`, that
becomes tedious and visually noisy when `T` is a long name. It is quite
easy to address this easy and common case, while not attempting to solve
this with full generality.

Fixes #11581.

Change-Id: Ifb8e43515f05493de190e02577260d94dd851581
Reviewed-on: https://go-review.googlesource.com/c/146577
Run-TryBot: David Symonds <dsymonds@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go
index 5edea7b..57d3a72 100644
--- a/cmd/stringer/stringer.go
+++ b/cmd/stringer/stringer.go
@@ -428,10 +428,24 @@
 	for _, spec := range decl.Specs {
 		vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
 		if vspec.Type == nil && len(vspec.Values) > 0 {
-			// "X = 1". With no type but a value, the constant is untyped.
-			// Skip this vspec and reset the remembered type.
+			// "X = 1". With no type but a value. If the constant is untyped,
+			// skip this vspec and reset the remembered type.
 			typ = ""
-			continue
+
+			// If this is a simple type conversion, remember the type.
+			// We don't mind if this is actually a call; a qualified call won't
+			// be matched (that will be SelectorExpr, not Ident), and only unusual
+			// situations will result in a function call that appears to be
+			// a type conversion.
+			ce, ok := vspec.Values[0].(*ast.CallExpr)
+			if !ok {
+				continue
+			}
+			id, ok := ce.Fun.(*ast.Ident)
+			if !ok {
+				continue
+			}
+			typ = id.Name
 		}
 		if vspec.Type != nil {
 			// "X T". We have a type. Remember it.
diff --git a/cmd/stringer/testdata/conv.go b/cmd/stringer/testdata/conv.go
new file mode 100644
index 0000000..9a9dc64
--- /dev/null
+++ b/cmd/stringer/testdata/conv.go
@@ -0,0 +1,41 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Check that constants defined as a conversion are accepted.
+
+package main
+
+import "fmt"
+
+type Other int // Imagine this is in another package.
+
+const (
+	alpha Other = iota
+	beta
+	gamma
+	delta
+)
+
+type Conv int
+
+const (
+	Alpha = Conv(alpha)
+	Beta  = Conv(beta)
+	Gamma = Conv(gamma)
+	Delta = Conv(delta)
+)
+
+func main() {
+	ck(Alpha, "Alpha")
+	ck(Beta, "Beta")
+	ck(Gamma, "Gamma")
+	ck(Delta, "Delta")
+	ck(42, "Conv(42)")
+}
+
+func ck(c Conv, str string) {
+	if fmt.Sprint(c) != str {
+		panic("conv.go: " + str)
+	}
+}