s1028.go raw
1 package s1028
2
3 import (
4 "go/ast"
5
6 "honnef.co/go/tools/analysis/code"
7 "honnef.co/go/tools/analysis/edit"
8 "honnef.co/go/tools/analysis/facts/generated"
9 "honnef.co/go/tools/analysis/lint"
10 "honnef.co/go/tools/analysis/report"
11 "honnef.co/go/tools/pattern"
12
13 "golang.org/x/tools/go/analysis"
14 "golang.org/x/tools/go/analysis/passes/inspect"
15 )
16
17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
18 Analyzer: &analysis.Analyzer{
19 Name: "S1028",
20 Run: run,
21 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
22 },
23 Doc: &lint.RawDocumentation{
24 Title: `Simplify error construction with \'fmt.Errorf\'`,
25 Before: `errors.New(fmt.Sprintf(...))`,
26 After: `fmt.Errorf(...)`,
27 Since: "2017.1",
28 MergeIf: lint.MergeIfAny,
29 },
30 })
31
32 var Analyzer = SCAnalyzer.Analyzer
33
34 var (
35 checkErrorsNewSprintfQ = pattern.MustParse(`(CallExpr (Symbol "errors.New") [(CallExpr (Symbol "fmt.Sprintf") args)])`)
36 checkErrorsNewSprintfR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "fmt") (Ident "Errorf")) args)`)
37 )
38
39 func run(pass *analysis.Pass) (interface{}, error) {
40 fn := func(node ast.Node) {
41 if _, edits, ok := code.MatchAndEdit(pass, checkErrorsNewSprintfQ, checkErrorsNewSprintfR, node); ok {
42 // TODO(dh): the suggested fix may leave an unused import behind
43 report.Report(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))",
44 report.FilterGenerated(),
45 report.Fixes(edit.Fix("use fmt.Errorf", edits...)))
46 }
47 }
48 code.Preorder(pass, fn, (*ast.CallExpr)(nil))
49 return nil, nil
50 }
51