qf1010.go raw
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 "golang.org/x/tools/go/analysis/passes/inspect"
16 )
17
18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
19 Analyzer: &analysis.Analyzer{
20 Name: "QF1010",
21 Run: run,
22 Requires: []*analysis.Analyzer{inspect.Analyzer},
23 },
24 Doc: &lint.RawDocumentation{
25 Title: "Convert slice of bytes to string when printing it",
26 Since: "2021.1",
27 Severity: lint.SeverityHint,
28 },
29 })
30
31 var Analyzer = SCAnalyzer.Analyzer
32
33 var byteSlicePrintingQ = pattern.MustParse(`
34 (Or
35 (CallExpr
36 (Symbol (Or
37 "fmt.Print"
38 "fmt.Println"
39 "fmt.Sprint"
40 "fmt.Sprintln"
41 "log.Fatal"
42 "log.Fatalln"
43 "log.Panic"
44 "log.Panicln"
45 "log.Print"
46 "log.Println"
47 "(*log.Logger).Fatal"
48 "(*log.Logger).Fatalln"
49 "(*log.Logger).Panic"
50 "(*log.Logger).Panicln"
51 "(*log.Logger).Print"
52 "(*log.Logger).Println")) args)
53
54 (CallExpr (Symbol (Or
55 "fmt.Fprint"
56 "fmt.Fprintln")) _:args))`)
57
58 var byteSlicePrintingR = pattern.MustParse(`(CallExpr (Ident "string") [arg])`)
59
60 func run(pass *analysis.Pass) (interface{}, error) {
61 fn := func(node ast.Node) {
62 m, ok := code.Match(pass, byteSlicePrintingQ, node)
63 if !ok {
64 return
65 }
66 args := m.State["args"].([]ast.Expr)
67 for _, arg := range args {
68 if !code.IsOfStringConvertibleByteSlice(pass, arg) {
69 continue
70 }
71 if types.Implements(pass.TypesInfo.TypeOf(arg), knowledge.Interfaces["fmt.Stringer"]) {
72 continue
73 }
74
75 fix := edit.Fix("Convert argument to string", edit.ReplaceWithPattern(pass.Fset, arg, byteSlicePrintingR, pattern.State{"arg": arg}))
76 report.Report(pass, arg, "could convert argument to string", report.Fixes(fix))
77 }
78 }
79 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
80 return nil, nil
81 }
82