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