1 package s1039
2 3 import (
4 "fmt"
5 "go/ast"
6 "go/types"
7 "strings"
8 9 "honnef.co/go/tools/analysis/code"
10 "honnef.co/go/tools/analysis/edit"
11 "honnef.co/go/tools/analysis/facts/generated"
12 "honnef.co/go/tools/analysis/lint"
13 "honnef.co/go/tools/analysis/report"
14 "honnef.co/go/tools/pattern"
15 16 "golang.org/x/tools/go/analysis"
17 "golang.org/x/tools/go/analysis/passes/inspect"
18 )
19 20 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
21 Analyzer: &analysis.Analyzer{
22 Name: "S1039",
23 Run: run,
24 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
25 },
26 Doc: &lint.RawDocumentation{
27 Title: `Unnecessary use of \'fmt.Sprint\'`,
28 Text: `
29 Calling \'fmt.Sprint\' with a single string argument is unnecessary
30 and identical to using the string directly.`,
31 Since: "2020.1",
32 // MergeIfAll because s might not be a string under all build tags.
33 // you shouldn't write code like that…
34 MergeIf: lint.MergeIfAll,
35 },
36 })
37 38 var Analyzer = SCAnalyzer.Analyzer
39 40 var checkSprintLiteralQ = pattern.MustParse(`
41 (CallExpr
42 fn@(Or
43 (Symbol "fmt.Sprint")
44 (Symbol "fmt.Sprintf"))
45 [lit@(BasicLit "STRING" _)])`)
46 47 func run(pass *analysis.Pass) (interface{}, error) {
48 // We only flag calls with string literals, not expressions of
49 // type string, because some people use fmt.Sprint(s) as a pattern
50 // for copying strings, which may be useful when extracting a small
51 // substring from a large string.
52 fn := func(node ast.Node) {
53 m, ok := code.Match(pass, checkSprintLiteralQ, node)
54 if !ok {
55 return
56 }
57 callee := m.State["fn"].(*types.Func)
58 lit := m.State["lit"].(*ast.BasicLit)
59 if callee.Name() == "Sprintf" {
60 if strings.ContainsRune(lit.Value, '%') {
61 // This might be a format string
62 return
63 }
64 }
65 report.Report(pass, node, fmt.Sprintf("unnecessary use of fmt.%s", callee.Name()),
66 report.FilterGenerated(),
67 report.Fixes(edit.Fix("Replace with string literal", edit.ReplaceWithNode(pass.Fset, node, lit))))
68 }
69 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
70 return nil, nil
71 }
72