x/tools: print check misses concatenated strings
Fixes golang/go#30436
Change-Id: I5b843e65b2188040a1ea5f17e1756b57babb0c22
GitHub-Last-Rev: 3ee8e1838c9dab095af0b8afa8177132ec8fdeea
GitHub-Pull-Request: golang/tools#344
Reviewed-on: https://go-review.googlesource.com/c/tools/+/356830
Trust: Tim King <taking@google.com>
Trust: Zvonimir Pavlinovic <zpavlinovic@google.com>
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go
index 5d508ac..2abbeda 100644
--- a/go/analysis/passes/printf/printf.go
+++ b/go/analysis/passes/printf/printf.go
@@ -452,8 +452,15 @@
if idx >= len(call.Args) {
return "", false
}
- arg := call.Args[idx]
- lit := pass.TypesInfo.Types[arg].Value
+ return stringConstantExpr(pass, call.Args[idx])
+}
+
+// stringConstantExpr returns expression's string constant value.
+//
+// ("", false) is returned if expression isn't a string
+// constant.
+func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
+ lit := pass.TypesInfo.Types[expr].Value
if lit != nil && lit.Kind() == constant.String {
return constant.StringVal(lit), true
}
@@ -1053,10 +1060,10 @@
}
arg := args[0]
- if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- // Ignore trailing % character in lit.Value.
+ if s, ok := stringConstantExpr(pass, arg); ok {
+ // Ignore trailing % character
// The % in "abc 0.0%" couldn't be a formatting directive.
- s := strings.TrimSuffix(lit.Value, `%"`)
+ s = strings.TrimSuffix(s, "%")
if strings.Contains(s, "%") {
m := printFormatRE.FindStringSubmatch(s)
if m != nil {
@@ -1067,9 +1074,8 @@
if strings.HasSuffix(fn.Name(), "ln") {
// The last item, if a string, should not have a newline.
arg = args[len(args)-1]
- if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- str, _ := strconv.Unquote(lit.Value)
- if strings.HasSuffix(str, "\n") {
+ if s, ok := stringConstantExpr(pass, arg); ok {
+ if strings.HasSuffix(s, "\n") {
pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName())
}
}
diff --git a/go/analysis/passes/printf/testdata/src/a/a.go b/go/analysis/passes/printf/testdata/src/a/a.go
index 568a19e..a2a85a9 100644
--- a/go/analysis/passes/printf/testdata/src/a/a.go
+++ b/go/analysis/passes/printf/testdata/src/a/a.go
@@ -153,6 +153,7 @@
fmt.Println("%s", "hi") // want "fmt.Println call has possible formatting directive %s"
fmt.Println("%v", "hi") // want "fmt.Println call has possible formatting directive %v"
fmt.Println("%T", "hi") // want "fmt.Println call has possible formatting directive %T"
+ fmt.Println("%s"+" there", "hi") // want "fmt.Println call has possible formatting directive %s"
fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive)
fmt.Printf("%s", "hi", 3) // want "fmt.Printf call needs 1 arg but has 2 args"
_ = fmt.Sprintf("%"+("s"), "hi", 3) // want "fmt.Sprintf call needs 1 arg but has 2 args"
@@ -768,9 +769,10 @@
fmt.Printf("%s", uei) // want "Printf format %s has arg uei of wrong type a.unexportedErrorInterface"
fmt.Println("foo\n", "bar") // not an error
- fmt.Println("foo\n") // want "Println arg list ends with redundant newline"
- fmt.Println("foo\\n") // not an error
- fmt.Println(`foo\n`) // not an error
+ fmt.Println("foo\n") // want "Println arg list ends with redundant newline"
+ fmt.Println("foo" + "\n") // want "Println arg list ends with redundant newline"
+ fmt.Println("foo\\n") // not an error
+ fmt.Println(`foo\n`) // not an error
intSlice := []int{3, 4}
fmt.Printf("%s", intSlice) // want `fmt.Printf format %s has arg intSlice of wrong type \[\]int`