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