aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeddy Wing2023-05-14 21:21:06 +0200
committerTeddy Wing2023-05-14 21:26:36 +0200
commit6d91d473f24d6825170e494852b26ffc748fe542 (patch)
treeb4d5ec4ca1f1a46390407b0023eebfe6232ca2b2
parent27b71d7c8fdedca306885d078be5a35a39669907 (diff)
downloadgocapturedrefrace-6d91d473f24d6825170e494852b26ffc748fe542.tar.bz2
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
-rw-r--r--.gitignore1
-rw-r--r--cmd/gocapturedrefrace/main.go10
-rw-r--r--go.mod10
-rw-r--r--go.sum7
-rw-r--r--gocapturedrefrace.go45
-rw-r--r--testdata/simple.go9
6 files changed, 82 insertions, 0 deletions
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
+ }()
+}