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)
+ }
+}