s1039.go (1903B)
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 ) 18 19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 20 Analyzer: &analysis.Analyzer{ 21 Name: "S1039", 22 Run: run, 23 Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...), 24 }, 25 Doc: &lint.RawDocumentation{ 26 Title: `Unnecessary use of \'fmt.Sprint\'`, 27 Text: ` 28 Calling \'fmt.Sprint\' with a single string argument is unnecessary 29 and identical to using the string directly.`, 30 Since: "2020.1", 31 // MergeIfAll because s might not be a string under all build tags. 32 // you shouldn't write code like that… 33 MergeIf: lint.MergeIfAll, 34 }, 35 }) 36 37 var Analyzer = SCAnalyzer.Analyzer 38 39 var checkSprintLiteralQ = pattern.MustParse(` 40 (CallExpr 41 fn@(Or 42 (Symbol "fmt.Sprint") 43 (Symbol "fmt.Sprintf")) 44 [lit@(BasicLit "STRING" _)])`) 45 46 func run(pass *analysis.Pass) (any, error) { 47 // We only flag calls with string literals, not expressions of 48 // type string, because some people use fmt.Sprint(s) as a pattern 49 // for copying strings, which may be useful when extracting a small 50 // substring from a large string. 51 52 for node, m := range code.Matches(pass, checkSprintLiteralQ) { 53 callee := m.State["fn"].(*types.Func) 54 lit := m.State["lit"].(*ast.BasicLit) 55 if callee.Name() == "Sprintf" { 56 if strings.ContainsRune(lit.Value, '%') { 57 // This might be a format string 58 continue 59 } 60 } 61 report.Report(pass, node, fmt.Sprintf("unnecessary use of fmt.%s", callee.Name()), 62 report.FilterGenerated(), 63 report.Fixes(edit.Fix("Replace with string literal", edit.ReplaceWithNode(pass.Fset, node, lit)))) 64 } 65 return nil, nil 66 }