aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeddy Wing2024-03-09 20:58:56 +0100
committerTeddy Wing2024-03-09 20:58:56 +0100
commitf923099c4302e49a1563183c5217e1e5cf017d85 (patch)
treecb52685aa7bcc7e917ebc1d67ca0e7f3276dc130
parentbf64f32a1a8099ee45d66796b5f60f9ae10a2436 (diff)
downloadgocapturedrefrace-f923099c4302e49a1563183c5217e1e5cf017d85.tar.bz2
Try checking if the same variable in inner scope is in parent scope
Can't figure out how to resolve the `funcScope != scope` problem in Go 1.22. Saw the `Names` method in the documentation and out of curiosity tried to see if that could be used as an alternate means of checking if a variable is declared in the parent scope. Turns out I get a bunch of false positives with that so I can't use it: $ go test -v === RUN Test identifier: &ast.Ident{NamePos:4069121, Name:"copied", Obj:(*ast.Object)(0x1400206b720)}, obj: &ast.Object{Kind:4, Name:"copied", Decl:(*ast.Field)(0x140067420c0), Data:interface {}(nil), Type:interface {}(nil)}, decl: &ast.Field{Doc:(*ast.CommentGroup)(nil), Names:[]*ast.Ident{(*ast.Ident)(0x1400215fea0)}, Type:(*ast.Ident)(0x1400215fec0), Tag:(*ast.BasicLit)(nil), Comment:(*ast.CommentGroup)(nil)} scope: &types.Scope{parent:(*types.Scope)(0x14002164960), children:[]*types.Scope{(*types.Scope)(0x14003b12d80)}, number:1, elems:map[string]types.Object{"capturedReference":(*types.Var)(0x14003b12c60), "capturedReference2":(*types.Var)(0x14003b12cc0), "copied":(*types.Var)(0x14003b12d20)}, pos:4069035, end:4069588, comment:"function", isFunc:true} Names: []string{"capturedReference", "capturedReference2", "copied"} funcScope: &types.Scope{parent:(*types.Scope)(0x14002165620), children:[]*types.Scope{(*types.Scope)(0x14003b12ea0)}, number:1, elems:map[string]types.Object{"copied":(*types.Var)(0x14003b12e40), "decl":(*types.Var)(0x14003b13020), "newVar":(*types.Var)(0x14003b12f60), "str":(*types.Var)(0x14003b12fc0)}, pos:4069116, end:4069578, comment:"function", isFunc:true} Names: []string{"copied", "decl", "newVar", "str"} identifier: &ast.Ident{NamePos:4069325, Name:"copied", Obj:(*ast.Object)(0x1400206b720)}, obj: &ast.Object{Kind:4, Name:"copied", Decl:(*ast.Field)(0x140067420c0), Data:interface {}(nil), Type:interface {}(nil)}, decl: &ast.Field{Doc:(*ast.CommentGroup)(nil), Names:[]*ast.Ident{(*ast.Ident)(0x1400215fea0)}, Type:(*ast.Ident)(0x1400215fec0), Tag:(*ast.BasicLit)(nil), Comment:(*ast.CommentGroup)(nil)} scope: &types.Scope{parent:(*types.Scope)(0x14002165620), children:[]*types.Scope{(*types.Scope)(0x14003b12ea0)}, number:1, elems:map[string]types.Object{"copied":(*types.Var)(0x14003b12e40), "decl":(*types.Var)(0x14003b13020), "newVar":(*types.Var)(0x14003b12f60), "str":(*types.Var)(0x14003b12fc0)}, pos:4069116, end:4069578, comment:"function", isFunc:true} Names: []string{"copied", "decl", "newVar", "str"} funcScope: &types.Scope{parent:(*types.Scope)(0x14002165620), children:[]*types.Scope{(*types.Scope)(0x14003b12ea0)}, number:1, elems:map[string]types.Object{"copied":(*types.Var)(0x14003b12e40), "decl":(*types.Var)(0x14003b13020), "newVar":(*types.Var)(0x14003b12f60), "str":(*types.Var)(0x14003b12fc0)}, pos:4069116, end:4069578, comment:"function", isFunc:true} Names: []string{"copied", "decl", "newVar", "str"} analysistest.go:522: gocapturedrefrace/testdata/shadow.go:38:6: unexpected diagnostic: captured reference err in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:39:14: unexpected diagnostic: captured reference err in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:47:3: unexpected diagnostic: captured reference err in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:48:6: unexpected diagnostic: captured reference err in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:49:14: unexpected diagnostic: captured reference err in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:66:3: unexpected diagnostic: captured reference err1 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:67:3: unexpected diagnostic: captured reference err2 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:68:6: unexpected diagnostic: captured reference err1 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:68:21: unexpected diagnostic: captured reference err2 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:69:14: unexpected diagnostic: captured reference err1 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/shadow.go:69:20: unexpected diagnostic: captured reference err2 in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:29:10: unexpected diagnostic: captured reference copied in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:32:3: unexpected diagnostic: captured reference copied in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:39:3: unexpected diagnostic: captured reference newVar in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:42:18: unexpected diagnostic: captured reference str in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:45:3: unexpected diagnostic: captured reference decl in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:46:18: unexpected diagnostic: captured reference decl in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:57:10: unexpected diagnostic: captured reference s in goroutine closure analysistest.go:522: gocapturedrefrace/testdata/simple.go:58:3: unexpected diagnostic: captured reference s in goroutine closure --- FAIL: Test (0.40s) FAIL exit status 1 FAIL gopkg.teddywing.com/capturedrefrace 0.414s
-rw-r--r--capturedrefrace.go27
-rw-r--r--go.mod7
-rw-r--r--go.sum5
3 files changed, 35 insertions, 4 deletions
diff --git a/capturedrefrace.go b/capturedrefrace.go
index 52f7c34..e155470 100644
--- a/capturedrefrace.go
+++ b/capturedrefrace.go
@@ -56,6 +56,7 @@ import (
"go/token"
"go/types"
+ "golang.org/x/exp/slices"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
@@ -221,14 +222,36 @@ func checkClosure(pass *analysis.Pass, funcLit *ast.FuncLit) {
if ident.Name == "copied" {
fmt.Printf("identifier: %#v, obj: %#v, decl: %#v\n", ident, ident.Obj, ident.Obj.Decl)
- fmt.Printf("scope: %#v\n", scope)
- fmt.Printf("funcScope: %#v\n", funcScope)
+ fmt.Printf(" scope: %#v\n", scope)
+ fmt.Printf(" Names: %#v\n", scope.Names())
+ fmt.Printf(" funcScope: %#v\n", funcScope)
+ fmt.Printf(" Names: %#v\n", funcScope.Names())
fmt.Println()
}
+ za := slices.Index(funcScope.Names(), ident.Name)
+ // if za == -1 {
+ // return true
+ // }
+ zb := slices.Index(scope.Names(), ident.Name)
+ // if zb == -1 {
+ // return true
+ // }
+
// TODO: Broken by golang/go a27a525d1b4df74989ac9f6ad10394391fe3eb88
// Test with golang.org/x/tools@v0.15.0
// Identifier was defined in a different scope.
+ // if funcScope != scope {
+ if za != -1 && zb != -1 && funcScope.Names()[za] == scope.Names()[zb] {
+ pass.Reportf(
+ ident.Pos(),
+ "captured reference %s in goroutine closure",
+ ident,
+ )
+
+ return true
+ }
+
if funcScope != scope {
pass.Reportf(
ident.Pos(),
diff --git a/go.mod b/go.mod
index f1d1907..58d3aba 100644
--- a/go.mod
+++ b/go.mod
@@ -2,9 +2,12 @@ module gopkg.teddywing.com/capturedrefrace
go 1.20
-require golang.org/x/tools v0.15.0
+require (
+ golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
+ golang.org/x/tools v0.18.0
+)
require (
golang.org/x/mod v0.16.0 // indirect
- golang.org/x/sys v0.14.0 // indirect
+ golang.org/x/sys v0.17.0 // indirect
)
diff --git a/go.sum b/go.sum
index 66bbf33..6659cbc 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,12 @@
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
+golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
+golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=