From 45ff765b4815d34d8b80220fd05c79063b185ce1 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Thu, 1 Nov 2018 14:33:50 +1100 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Rob Pike --- cmd/stringer/stringer.go | 20 ++++++++++++++--- cmd/stringer/testdata/conv.go | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 cmd/stringer/testdata/conv.go diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go index 5edea7bf..57d3a723 100644 --- a/cmd/stringer/stringer.go +++ b/cmd/stringer/stringer.go @@ -428,10 +428,24 @@ func (f *File) genDecl(node ast.Node) bool { 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 00000000..9a9dc644 --- /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) + } +}