From 6d91d473f24d6825170e494852b26ffc748fe542 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sun, 14 May 2023 21:21:06 +0200 Subject: Find `go` statements using Go analyzer Build a basic Go analyser that finds `go` statements. Currently testing this with: $ go run ./cmd/gocapturedrefrace ./testdata/ Using the following tutorials as a guide: * https://arslan.io/2019/06/13/using-go-analysis-to-write-a-custom-linter/ * https://scribe.rip/codex/writing-custom-linter-in-go-54ef6f8080 --- .gitignore | 1 + cmd/gocapturedrefrace/main.go | 10 ++++++++++ go.mod | 10 ++++++++++ go.sum | 7 +++++++ gocapturedrefrace.go | 45 +++++++++++++++++++++++++++++++++++++++++++ testdata/simple.go | 9 +++++++++ 6 files changed, 82 insertions(+) create mode 100644 .gitignore create mode 100644 cmd/gocapturedrefrace/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 gocapturedrefrace.go create mode 100644 testdata/simple.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd1a426 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/private/ diff --git a/cmd/gocapturedrefrace/main.go b/cmd/gocapturedrefrace/main.go new file mode 100644 index 0000000..d637a38 --- /dev/null +++ b/cmd/gocapturedrefrace/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "git.teddywing.com/gocapturedrefrace" + "golang.org/x/tools/go/analysis/singlechecker" +) + +func main() { + singlechecker.Main(gocapturedrefrace.Analyzer) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..218c9d0 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module git.teddywing.com/gocapturedrefrace + +go 1.20 + +require golang.org/x/tools v0.9.1 + +require ( + golang.org/x/mod v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cf10794 --- /dev/null +++ b/go.sum @@ -0,0 +1,7 @@ +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= diff --git a/gocapturedrefrace.go b/gocapturedrefrace.go new file mode 100644 index 0000000..762473b --- /dev/null +++ b/gocapturedrefrace.go @@ -0,0 +1,45 @@ +package gocapturedrefrace + +import ( + "bytes" + "go/ast" + "go/printer" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "gocapturedrefrace", + Doc: "reports captured references in goroutine closures", + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, file := range pass.Files { + ast.Inspect( + file, + func(node ast.Node) bool { + goStmt, ok := node.(*ast.GoStmt) + if !ok { + return true + } + + var printedNode bytes.Buffer + err := printer.Fprint(&printedNode, pass.Fset, goStmt) + if err != nil { + panic(err) + } + + pass.Reportf( + goStmt.Pos(), + "go statement found %q", + printedNode, + ) + + return true + }, + ) + } + + return nil, nil +} diff --git a/testdata/simple.go b/testdata/simple.go new file mode 100644 index 0000000..496be52 --- /dev/null +++ b/testdata/simple.go @@ -0,0 +1,9 @@ +package main + +func main() { + capturedReference := 0 + + go func() { + capturedReference += 1 + }() +} -- cgit v1.2.3