github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/simple/s1039/s1039.go (about) 1 package s1039 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/types" 7 "strings" 8 9 "github.com/amarpal/go-tools/analysis/code" 10 "github.com/amarpal/go-tools/analysis/edit" 11 "github.com/amarpal/go-tools/analysis/facts/generated" 12 "github.com/amarpal/go-tools/analysis/lint" 13 "github.com/amarpal/go-tools/analysis/report" 14 "github.com/amarpal/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.Documentation{ 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 }