cmd/stringer: add a few tests using instantiated generic types

The added tests are in a subdirectory of testdir that is looked
at only if typeparams.Enabled is true, but some test helper routines
needed to be modified to cope with files in a subdirectory.

Change-Id: I82c22a8ba74a1e4a1a2c8669c2271449d5fcaf3a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/355313
Run-TryBot: Peter Weinberger <pjw@google.com>
Trust: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/cmd/stringer/endtoend_test.go b/cmd/stringer/endtoend_test.go
index 3b0b39d..5b969a5 100644
--- a/cmd/stringer/endtoend_test.go
+++ b/cmd/stringer/endtoend_test.go
@@ -17,11 +17,13 @@
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"strings"
 	"testing"
 
 	"golang.org/x/tools/internal/testenv"
+	"golang.org/x/tools/internal/typeparams"
 )
 
 // This file contains a test that compiles and runs each program in testdata
@@ -42,8 +44,15 @@
 	if err != nil {
 		t.Fatalf("Readdirnames: %s", err)
 	}
+	if typeparams.Enabled {
+		names = append(names, moreTests(t, "testdata/typeparams", "typeparams")...)
+	}
 	// Generate, compile, and run the test programs.
 	for _, name := range names {
+		if name == "typeparams" {
+			// ignore the directory containing the tests with type params
+			continue
+		}
 		if !strings.HasSuffix(name, ".go") {
 			t.Errorf("%s is not a Go file", name)
 			continue
@@ -56,12 +65,31 @@
 			t.Logf("cgo is not enabled for %s", name)
 			continue
 		}
-		// Names are known to be ASCII and long enough.
-		typeName := fmt.Sprintf("%c%s", name[0]+'A'-'a', name[1:len(name)-len(".go")])
-		stringerCompileAndRun(t, dir, stringer, typeName, name)
+		stringerCompileAndRun(t, dir, stringer, typeName(name), name)
 	}
 }
 
+// a type name for stringer. use the last component of the file name with the .go
+func typeName(fname string) string {
+	// file names are known to be ascii and end .go
+	base := path.Base(fname)
+	return fmt.Sprintf("%c%s", base[0]+'A'-'a', base[1:len(base)-len(".go")])
+}
+
+func moreTests(t *testing.T, dirname, prefix string) []string {
+	x, err := os.ReadDir(dirname)
+	if err != nil {
+		// error, but try the rest of the tests
+		t.Errorf("can't read type param tess from %s: %v", dirname, err)
+		return nil
+	}
+	names := make([]string, len(x))
+	for i, f := range x {
+		names[i] = prefix + "/" + f.Name()
+	}
+	return names
+}
+
 // TestTags verifies that the -tags flag works as advertised.
 func TestTags(t *testing.T) {
 	dir, stringer := buildStringer(t)
@@ -173,7 +201,7 @@
 func stringerCompileAndRun(t *testing.T, dir, stringer, typeName, fileName string) {
 	t.Helper()
 	t.Logf("run: %s %s\n", fileName, typeName)
-	source := filepath.Join(dir, fileName)
+	source := filepath.Join(dir, path.Base(fileName))
 	err := copy(source, filepath.Join("testdata", fileName))
 	if err != nil {
 		t.Fatalf("copying file to temporary directory: %s", err)
diff --git a/cmd/stringer/testdata/typeparams/conv2.go b/cmd/stringer/testdata/typeparams/conv2.go
new file mode 100644
index 0000000..1563fe8
--- /dev/null
+++ b/cmd/stringer/testdata/typeparams/conv2.go
@@ -0,0 +1,43 @@
+// Copyright 2021 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.
+
+// This is a version of ../conv.go with type params
+
+// Check that constants defined as a conversion are accepted.
+
+package main
+
+import "fmt"
+
+type Other[T interface{ ~int | ~uint }] T // Imagine this is in another package.
+
+const (
+	alpha Other[int] = iota
+	beta
+	gamma
+	delta
+)
+
+type Conv2 Other[int]
+
+const (
+	Alpha = Conv2(alpha)
+	Beta  = Conv2(beta)
+	Gamma = Conv2(gamma)
+	Delta = Conv2(delta)
+)
+
+func main() {
+	ck(Alpha, "Alpha")
+	ck(Beta, "Beta")
+	ck(Gamma, "Gamma")
+	ck(Delta, "Delta")
+	ck(42, "Conv2(42)")
+}
+
+func ck(c Conv2, str string) {
+	if fmt.Sprint(c) != str {
+		panic("conv2.go: " + str)
+	}
+}
diff --git a/cmd/stringer/testdata/typeparams/prime2.go b/cmd/stringer/testdata/typeparams/prime2.go
new file mode 100644
index 0000000..376c9e2
--- /dev/null
+++ b/cmd/stringer/testdata/typeparams/prime2.go
@@ -0,0 +1,60 @@
+// Copyright 2021 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.
+
+// This is a version of ../prime.go with type params
+
+// Enough gaps to trigger a map implementation of the method.
+// Also includes a duplicate to test that it doesn't cause problems
+
+package main
+
+import "fmt"
+
+type Likeint[T interface{ ~int | ~uint8 }] T
+
+type Prime2 Likeint[int]
+
+const (
+	p2  Prime2 = 2
+	p3  Prime2 = 3
+	p5  Prime2 = 5
+	p7  Prime2 = 7
+	p77 Prime2 = 7 // Duplicate; note that p77 doesn't appear below.
+	p11 Prime2 = 11
+	p13 Prime2 = 13
+	p17 Prime2 = 17
+	p19 Prime2 = 19
+	p23 Prime2 = 23
+	p29 Prime2 = 29
+	p37 Prime2 = 31
+	p41 Prime2 = 41
+	p43 Prime2 = 43
+)
+
+func main() {
+	ck(0, "Prime2(0)")
+	ck(1, "Prime2(1)")
+	ck(p2, "p2")
+	ck(p3, "p3")
+	ck(4, "Prime2(4)")
+	ck(p5, "p5")
+	ck(p7, "p7")
+	ck(p77, "p7")
+	ck(p11, "p11")
+	ck(p13, "p13")
+	ck(p17, "p17")
+	ck(p19, "p19")
+	ck(p23, "p23")
+	ck(p29, "p29")
+	ck(p37, "p37")
+	ck(p41, "p41")
+	ck(p43, "p43")
+	ck(44, "Prime2(44)")
+}
+
+func ck(prime Prime2, str string) {
+	if fmt.Sprint(prime) != str {
+		panic("prime2.go: " + str)
+	}
+}