sa3001.go raw

   1  package sa3001
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  
   7  	"honnef.co/go/tools/analysis/code"
   8  	"honnef.co/go/tools/analysis/lint"
   9  	"honnef.co/go/tools/analysis/report"
  10  
  11  	"golang.org/x/tools/go/analysis"
  12  	"golang.org/x/tools/go/analysis/passes/inspect"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name:     "SA3001",
  18  		Run:      run,
  19  		Requires: []*analysis.Analyzer{inspect.Analyzer},
  20  	},
  21  	Doc: &lint.RawDocumentation{
  22  		Title: `Assigning to \'b.N\' in benchmarks distorts the results`,
  23  		Text: `The testing package dynamically sets \'b.N\' to improve the reliability of
  24  benchmarks and uses it in computations to determine the duration of a
  25  single operation. Benchmark code must not alter \'b.N\' as this would
  26  falsify results.`,
  27  		Since:    "2017.1",
  28  		Severity: lint.SeverityError,
  29  		MergeIf:  lint.MergeIfAny,
  30  	},
  31  })
  32  
  33  var Analyzer = SCAnalyzer.Analyzer
  34  
  35  func run(pass *analysis.Pass) (interface{}, error) {
  36  	fn := func(node ast.Node) {
  37  		assign := node.(*ast.AssignStmt)
  38  		if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
  39  			return
  40  		}
  41  		sel, ok := assign.Lhs[0].(*ast.SelectorExpr)
  42  		if !ok {
  43  			return
  44  		}
  45  		if sel.Sel.Name != "N" {
  46  			return
  47  		}
  48  		if !code.IsOfPointerToTypeWithName(pass, sel.X, "testing.B") {
  49  			return
  50  		}
  51  		report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel)))
  52  	}
  53  	code.Preorder(pass, fn, (*ast.AssignStmt)(nil))
  54  	return nil, nil
  55  }
  56