go/analysis: improve error message for string(int) warning

To an experienced Go user, the previous error message doesn't describe
the error--it merely states a fact. The new error message asks if the
user meant fmt.Sprint(x). If they used the conversion correctly, the
suggested fix to replace string(int) with string(rune(int)) is
understandable and valid. On the other hand, if they used it to print
a digit representation, asking that in the error message further
emphasizes the mistake.

Updates golang/go#39151.

Change-Id: Iab9cdcaf53e9ca134285246fad0546d6f24c3983
Reviewed-on: https://go-review.googlesource.com/c/tools/+/235797
Run-TryBot: Akhil Indurti <aindurti@gmail.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/go/analysis/passes/stringintconv/string.go b/go/analysis/passes/stringintconv/string.go
index ac2cd84..7a00590 100644
--- a/go/analysis/passes/stringintconv/string.go
+++ b/go/analysis/passes/stringintconv/string.go
@@ -101,7 +101,7 @@
 		}
 		diag := analysis.Diagnostic{
 			Pos:     n.Pos(),
-			Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune", source, target),
+			Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
 			SuggestedFixes: []analysis.SuggestedFix{
 				{
 					Message: "Did you mean to convert a rune to a string?",
diff --git a/go/analysis/passes/stringintconv/testdata/src/a/a.go b/go/analysis/passes/stringintconv/testdata/src/a/a.go
index 72ceb97..837469c 100644
--- a/go/analysis/passes/stringintconv/testdata/src/a/a.go
+++ b/go/analysis/passes/stringintconv/testdata/src/a/a.go
@@ -25,12 +25,12 @@
 		o struct{ x int }
 	)
 	const p = 0
-	_ = string(i) // want `^conversion from int to string yields a string of one rune$`
+	_ = string(i) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 	_ = string(j)
 	_ = string(k)
-	_ = string(p)    // want `^conversion from untyped int to string yields a string of one rune$`
-	_ = A(l)         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune$`
-	_ = B(m)         // want `^conversion from uintptr to B \(string\) yields a string of one rune$`
-	_ = string(n[1]) // want `^conversion from int to string yields a string of one rune$`
-	_ = string(o.x)  // want `^conversion from int to string yields a string of one rune$`
+	_ = string(p)    // want `^conversion from untyped int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = A(l)         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = B(m)         // want `^conversion from uintptr to B \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(n[1]) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(o.x)  // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 }
diff --git a/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden b/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
index 9538a01..593962d 100644
--- a/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
+++ b/go/analysis/passes/stringintconv/testdata/src/a/a.go.golden
@@ -25,12 +25,12 @@
 		o struct{ x int }
 	)
 	const p = 0
-	_ = string(rune(i)) // want `^conversion from int to string yields a string of one rune$`
+	_ = string(rune(i)) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 	_ = string(j)
 	_ = string(k)
-	_ = string(rune(p))    // want `^conversion from untyped int to string yields a string of one rune$`
-	_ = A(rune(l))         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune$`
-	_ = B(rune(m))         // want `^conversion from uintptr to B \(string\) yields a string of one rune$`
-	_ = string(rune(n[1])) // want `^conversion from int to string yields a string of one rune$`
-	_ = string(rune(o.x))  // want `^conversion from int to string yields a string of one rune$`
+	_ = string(rune(p))    // want `^conversion from untyped int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = A(rune(l))         // want `^conversion from C \(int\) to A \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = B(rune(m))         // want `^conversion from uintptr to B \(string\) yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(rune(n[1])) // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
+	_ = string(rune(o.x))  // want `^conversion from int to string yields a string of one rune, not a string of digits \(did you mean fmt\.Sprint\(x\)\?\)$`
 }