s1005.go raw
1 package s1005
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/facts/generated"
10 "honnef.co/go/tools/analysis/lint"
11 "honnef.co/go/tools/analysis/report"
12 "honnef.co/go/tools/go/ast/astutil"
13 "honnef.co/go/tools/pattern"
14
15 "golang.org/x/tools/go/analysis"
16 "golang.org/x/tools/go/analysis/passes/inspect"
17 )
18
19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
20 Analyzer: &analysis.Analyzer{
21 Name: "S1005",
22 Run: run,
23 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
24 },
25 Doc: &lint.RawDocumentation{
26 Title: `Drop unnecessary use of the blank identifier`,
27 Text: `In many cases, assigning to the blank identifier is unnecessary.`,
28 Before: `
29 for _ = range s {}
30 x, _ = someMap[key]
31 _ = <-ch`,
32 After: `
33 for range s{}
34 x = someMap[key]
35 <-ch`,
36 Since: "2017.1",
37 MergeIf: lint.MergeIfAny,
38 },
39 })
40
41 var Analyzer = SCAnalyzer.Analyzer
42
43 var (
44 checkUnnecessaryBlankQ1 = pattern.MustParse(`
45 (AssignStmt
46 [_ (Ident "_")]
47 _
48 (Or
49 (IndexExpr _ _)
50 (UnaryExpr "<-" _))) `)
51 checkUnnecessaryBlankQ2 = pattern.MustParse(`
52 (AssignStmt
53 (Ident "_") _ recv@(UnaryExpr "<-" _))`)
54 )
55
56 func run(pass *analysis.Pass) (interface{}, error) {
57 fn1 := func(node ast.Node) {
58 if _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok {
59 r := *node.(*ast.AssignStmt)
60 r.Lhs = r.Lhs[0:1]
61 report.Report(pass, node, "unnecessary assignment to the blank identifier",
62 report.FilterGenerated(),
63 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.ReplaceWithNode(pass.Fset, node, &r))))
64 } else if m, ok := code.Match(pass, checkUnnecessaryBlankQ2, node); ok {
65 report.Report(pass, node, "unnecessary assignment to the blank identifier",
66 report.FilterGenerated(),
67 report.Fixes(edit.Fix("simplify channel receive operation", edit.ReplaceWithNode(pass.Fset, node, m.State["recv"].(ast.Node)))))
68 }
69 }
70
71 fn3 := func(node ast.Node) {
72 rs := node.(*ast.RangeStmt)
73
74 if _, ok := pass.TypesInfo.TypeOf(rs.X).Underlying().(*types.Signature); ok {
75 // iteration variables are not optional with rangefunc
76 return
77 }
78
79 // for _
80 if rs.Value == nil && astutil.IsBlank(rs.Key) {
81 report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",
82 report.FilterGenerated(),
83 report.MinimumLanguageVersion("go1.4"),
84 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))
85 }
86
87 // for _, _
88 if astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {
89 // FIXME we should mark both key and value
90 report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",
91 report.FilterGenerated(),
92 report.MinimumLanguageVersion("go1.4"),
93 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))
94 }
95
96 // for x, _
97 if !astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {
98 report.Report(pass, rs.Value, "unnecessary assignment to the blank identifier",
99 report.FilterGenerated(),
100 report.MinimumLanguageVersion("go1.4"),
101 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()}))))
102 }
103 }
104
105 code.Preorder(pass, fn1, (*ast.AssignStmt)(nil))
106 code.Preorder(pass, fn3, (*ast.RangeStmt)(nil))
107 return nil, nil
108 }
109