sa6000.go raw

   1  package sa6000
   2  
   3  import (
   4  	"fmt"
   5  
   6  	"honnef.co/go/tools/analysis/callcheck"
   7  	"honnef.co/go/tools/analysis/lint"
   8  	"honnef.co/go/tools/go/ir"
   9  	"honnef.co/go/tools/go/ir/irutil"
  10  	"honnef.co/go/tools/internal/passes/buildir"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name:     "SA6000",
  18  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  19  		Run:      callcheck.Analyzer(rules),
  20  	},
  21  	Doc: &lint.RawDocumentation{
  22  		Title:    `Using \'regexp.Match\' or related in a loop, should use \'regexp.Compile\'`,
  23  		Since:    "2017.1",
  24  		Severity: lint.SeverityWarning,
  25  		MergeIf:  lint.MergeIfAny,
  26  	},
  27  })
  28  
  29  var Analyzer = SCAnalyzer.Analyzer
  30  
  31  var rules = map[string]callcheck.Check{
  32  	"regexp.Match":       check("regexp.Match"),
  33  	"regexp.MatchReader": check("regexp.MatchReader"),
  34  	"regexp.MatchString": check("regexp.MatchString"),
  35  }
  36  
  37  func check(name string) callcheck.Check {
  38  	return func(call *callcheck.Call) {
  39  		if callcheck.ExtractConst(call.Args[0].Value) == nil {
  40  			return
  41  		}
  42  		if !isInLoop(call.Instr.Block()) {
  43  			return
  44  		}
  45  		call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name))
  46  	}
  47  }
  48  
  49  func isInLoop(b *ir.BasicBlock) bool {
  50  	sets := irutil.FindLoops(b.Parent())
  51  	for _, set := range sets {
  52  		if set.Has(b) {
  53  			return true
  54  		}
  55  	}
  56  	return false
  57  }
  58