s1000.go raw
1 package s1000
2
3 import (
4 "go/ast"
5
6 "honnef.co/go/tools/analysis/code"
7 "honnef.co/go/tools/analysis/facts/generated"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/go/tools/analysis/report"
10 "honnef.co/go/tools/pattern"
11
12 "golang.org/x/tools/go/analysis"
13 "golang.org/x/tools/go/analysis/passes/inspect"
14 )
15
16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
17 Analyzer: &analysis.Analyzer{
18 Name: "S1000",
19 Run: run,
20 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
21 },
22 Doc: &lint.RawDocumentation{
23 Title: `Use plain channel send or receive instead of single-case select`,
24 Text: `Select statements with a single case can be replaced with a simple
25 send or receive.`,
26 Before: `
27 select {
28 case x := <-ch:
29 fmt.Println(x)
30 }`,
31 After: `
32 x := <-ch
33 fmt.Println(x)
34 `,
35 Since: "2017.1",
36 MergeIf: lint.MergeIfAny,
37 },
38 })
39
40 var Analyzer = SCAnalyzer.Analyzer
41
42 var (
43 checkSingleCaseSelectQ1 = pattern.MustParse(`
44 (ForStmt
45 nil nil nil
46 select@(SelectStmt
47 (CommClause
48 (Or
49 (UnaryExpr "<-" _)
50 (AssignStmt _ _ (UnaryExpr "<-" _)))
51 _)))`)
52 checkSingleCaseSelectQ2 = pattern.MustParse(`(SelectStmt (CommClause _ _))`)
53 )
54
55 func run(pass *analysis.Pass) (interface{}, error) {
56 seen := map[ast.Node]struct{}{}
57 fn := func(node ast.Node) {
58 if m, ok := code.Match(pass, checkSingleCaseSelectQ1, node); ok {
59 seen[m.State["select"].(ast.Node)] = struct{}{}
60 report.Report(pass, node, "should use for range instead of for { select {} }", report.FilterGenerated())
61 } else if _, ok := code.Match(pass, checkSingleCaseSelectQ2, node); ok {
62 if _, ok := seen[node]; !ok {
63 report.Report(pass, node, "should use a simple channel send/receive instead of select with a single case",
64 report.ShortRange(),
65 report.FilterGenerated())
66 }
67 }
68 }
69 code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.SelectStmt)(nil))
70 return nil, nil
71 }
72