sa2000.go raw
1 package sa2000
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 "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: "SA2000",
19 Run: run,
20 Requires: []*analysis.Analyzer{inspect.Analyzer},
21 },
22 Doc: &lint.RawDocumentation{
23 Title: `\'sync.WaitGroup.Add\' called inside the goroutine, leading to a race condition`,
24 Since: "2017.1",
25 Severity: lint.SeverityWarning,
26 MergeIf: lint.MergeIfAny,
27 },
28 })
29
30 var Analyzer = SCAnalyzer.Analyzer
31
32 var checkWaitgroupAddQ = pattern.MustParse(`
33 (GoStmt
34 (CallExpr
35 (FuncLit
36 _
37 call@(CallExpr (Symbol "(*sync.WaitGroup).Add") _):_) _))`)
38
39 func run(pass *analysis.Pass) (interface{}, error) {
40 fn := func(node ast.Node) {
41 if m, ok := code.Match(pass, checkWaitgroupAddQ, node); ok {
42 call := m.State["call"].(ast.Node)
43 report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call)))
44 }
45 }
46 code.Preorder(pass, fn, (*ast.GoStmt)(nil))
47 return nil, nil
48 }
49