qf1010.go (1829B)
1 package qf1010 2 3 import ( 4 "go/ast" 5 "go/types" 6 7 "honnef.co/go/tools/analysis/code" 8 "honnef.co/go/tools/analysis/edit" 9 "honnef.co/go/tools/analysis/lint" 10 "honnef.co/go/tools/analysis/report" 11 "honnef.co/go/tools/knowledge" 12 "honnef.co/go/tools/pattern" 13 14 "golang.org/x/tools/go/analysis" 15 ) 16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 18 Analyzer: &analysis.Analyzer{ 19 Name: "QF1010", 20 Run: run, 21 Requires: code.RequiredAnalyzers, 22 }, 23 Doc: &lint.RawDocumentation{ 24 Title: "Convert slice of bytes to string when printing it", 25 Since: "2021.1", 26 Severity: lint.SeverityHint, 27 }, 28 }) 29 30 var Analyzer = SCAnalyzer.Analyzer 31 32 var byteSlicePrintingQ = pattern.MustParse(` 33 (Or 34 (CallExpr 35 (Symbol (Or 36 "fmt.Print" 37 "fmt.Println" 38 "fmt.Sprint" 39 "fmt.Sprintln" 40 "log.Fatal" 41 "log.Fatalln" 42 "log.Panic" 43 "log.Panicln" 44 "log.Print" 45 "log.Println" 46 "(*log.Logger).Fatal" 47 "(*log.Logger).Fatalln" 48 "(*log.Logger).Panic" 49 "(*log.Logger).Panicln" 50 "(*log.Logger).Print" 51 "(*log.Logger).Println")) args) 52 53 (CallExpr (Symbol (Or 54 "fmt.Fprint" 55 "fmt.Fprintln")) _:args))`) 56 57 var byteSlicePrintingR = pattern.MustParse(`(CallExpr (Ident "string") [arg])`) 58 59 func run(pass *analysis.Pass) (any, error) { 60 for _, m := range code.Matches(pass, byteSlicePrintingQ) { 61 args := m.State["args"].([]ast.Expr) 62 for _, arg := range args { 63 if !code.IsOfStringConvertibleByteSlice(pass, arg) { 64 continue 65 } 66 if types.Implements(pass.TypesInfo.TypeOf(arg), knowledge.Interfaces["fmt.Stringer"]) { 67 continue 68 } 69 70 fix := edit.Fix("Convert argument to string", edit.ReplaceWithPattern(pass.Fset, arg, byteSlicePrintingR, pattern.State{"arg": arg})) 71 report.Report(pass, arg, "could convert argument to string", report.Fixes(fix)) 72 } 73 } 74 return nil, nil 75 }