From 3ee8e1838c9dab095af0b8afa8177132ec8fdeea Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 19 Oct 2021 17:27:02 +0200 Subject: [PATCH] x/tools: print check misses concatenated strings Fixes golang/go#30436 --- go/analysis/passes/printf/printf.go | 22 ++++++++++++------- go/analysis/passes/printf/testdata/src/a/a.go | 8 ++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go index 4169d30e4fc..8b68105efd4 100644 --- a/go/analysis/passes/printf/printf.go +++ b/go/analysis/passes/printf/printf.go @@ -452,8 +452,15 @@ func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string 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 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { } 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 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { 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 568a19e4fa6..a2a85a9d5a1 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 @@ func PrintfTests() { 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 @@ func UnexportedStringerOrError() { 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`