github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/quickfix/qf1010/qf1010.go (about) 1 package qf1010 2 3 import ( 4 "go/ast" 5 "go/types" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/edit" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/go-tools/analysis/report" 11 "github.com/amarpal/go-tools/go/types/typeutil" 12 "github.com/amarpal/go-tools/knowledge" 13 "github.com/amarpal/go-tools/pattern" 14 15 "golang.org/x/tools/go/analysis" 16 "golang.org/x/tools/go/analysis/passes/inspect" 17 ) 18 19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 20 Analyzer: &analysis.Analyzer{ 21 Name: "QF1010", 22 Run: run, 23 Requires: []*analysis.Analyzer{inspect.Analyzer}, 24 }, 25 Doc: &lint.Documentation{ 26 Title: "Convert slice of bytes to string when printing it", 27 Since: "2021.1", 28 Severity: lint.SeverityHint, 29 }, 30 }) 31 32 var Analyzer = SCAnalyzer.Analyzer 33 34 var byteSlicePrintingQ = pattern.MustParse(` 35 (Or 36 (CallExpr 37 (Symbol (Or 38 "fmt.Print" 39 "fmt.Println" 40 "fmt.Sprint" 41 "fmt.Sprintln" 42 "log.Fatal" 43 "log.Fatalln" 44 "log.Panic" 45 "log.Panicln" 46 "log.Print" 47 "log.Println" 48 "(*log.Logger).Fatal" 49 "(*log.Logger).Fatalln" 50 "(*log.Logger).Panic" 51 "(*log.Logger).Panicln" 52 "(*log.Logger).Print" 53 "(*log.Logger).Println")) args) 54 55 (CallExpr (Symbol (Or 56 "fmt.Fprint" 57 "fmt.Fprintln")) _:args))`) 58 59 var byteSlicePrintingR = pattern.MustParse(`(CallExpr (Ident "string") [arg])`) 60 61 func run(pass *analysis.Pass) (interface{}, error) { 62 fn := func(node ast.Node) { 63 m, ok := code.Match(pass, byteSlicePrintingQ, node) 64 if !ok { 65 return 66 } 67 args := m.State["args"].([]ast.Expr) 68 for _, arg := range args { 69 T := pass.TypesInfo.TypeOf(arg) 70 if typeutil.IsType(T.Underlying(), "[]byte") { 71 // don't convert arguments that implement fmt.Stringer 72 if types.Implements(T, knowledge.Interfaces["fmt.Stringer"]) { 73 continue 74 } 75 76 fix := edit.Fix("Convert argument to string", edit.ReplaceWithPattern(pass.Fset, arg, byteSlicePrintingR, pattern.State{"arg": arg})) 77 report.Report(pass, arg, "could convert argument to string", report.Fixes(fix)) 78 } 79 } 80 } 81 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 82 return nil, nil 83 }