diff options
author | Teddy Wing | 2023-05-18 04:52:21 +0200 |
---|---|---|
committer | Teddy Wing | 2023-05-18 04:52:21 +0200 |
commit | 75c1da0c3f0a1245e0b1a2775245d85a831eee47 (patch) | |
tree | 07802ac7d3996ec1585c30b5ceb14ca32d36d49c | |
parent | 3e2c455312e3558f6793b6f526c37820e8fe2afd (diff) | |
download | gocapturedrefrace-75c1da0c3f0a1245e0b1a2775245d85a831eee47.tar.bz2 |
Use `passes/inspect` to find `go` statements
I originally thought that I could save on the two additional
`ast.Inspect` calls in this file by using the `passes/inspect` library,
but it doesn't replace those calls. It seems to help more when you have
multiple analysers to run at once. Not sure how much it helps us here.
-rw-r--r-- | gocapturedrefrace.go | 76 |
1 files changed, 40 insertions, 36 deletions
diff --git a/gocapturedrefrace.go b/gocapturedrefrace.go index 1404f78..a3c1699 100644 --- a/gocapturedrefrace.go +++ b/gocapturedrefrace.go @@ -25,55 +25,59 @@ import ( "go/types" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" ) var version = "0.0.1" var Analyzer = &analysis.Analyzer{ - Name: "gocapturedrefrace", - Doc: "reports captured references in goroutine closures", - Run: run, + Name: "gocapturedrefrace", + Doc: "reports captured references in goroutine closures", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, } func run(pass *analysis.Pass) (interface{}, error) { - // TODO: Since we're calling ast.Inspect a bunch of times, maybe it's worthwhile using passes/inspect now. - for _, file := range pass.Files { - ast.Inspect( - file, - func(node ast.Node) bool { - // Find `go` statements. - goStmt, ok := node.(*ast.GoStmt) - if !ok { - return true - } + inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - // Look for a function literal after the `go` statement. - funcLit, ok := goStmt.Call.Fun.(*ast.FuncLit) - if !ok { - return true - } + nodeFilter := []ast.Node{ + (*ast.GoStmt)(nil), + } - // Inspect closure argument list. - for _, arg := range funcLit.Type.Params.List { - // Report reference arguments. - _, ok := arg.Type.(*ast.StarExpr) - if !ok { - continue - } - - pass.Reportf( - arg.Pos(), - "reference %s in goroutine closure", - arg.Names[0], - ) + inspect.Preorder( + nodeFilter, + func(node ast.Node) { + // Find `go` statements. + goStmt, ok := node.(*ast.GoStmt) + if !ok { + return + } + + // Look for a function literal after the `go` statement. + funcLit, ok := goStmt.Call.Fun.(*ast.FuncLit) + if !ok { + return + } + + // Inspect closure argument list. + for _, arg := range funcLit.Type.Params.List { + // Report reference arguments. + _, ok := arg.Type.(*ast.StarExpr) + if !ok { + continue } - checkClosure(pass, funcLit) + pass.Reportf( + arg.Pos(), + "reference %s in goroutine closure", + arg.Names[0], + ) + } - return true - }, - ) - } + checkClosure(pass, funcLit) + }, + ) return nil, nil } |