From bb532cefbda6e3a8a592d81c4053a30f97f106f3 Mon Sep 17 00:00:00 2001 From: tenntenn Date: Thu, 1 Oct 2020 18:24:36 +0900 Subject: [PATCH 1/4] Add Used --- ssa.go | 60 ++++++++++++++++++++++++++++++++++++++- ssa_test.go | 27 ++++++++++++++++++ testdata/src/used/used.go | 48 +++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 testdata/src/used/used.go diff --git a/ssa.go b/ssa.go index ce3af58..3140dce 100644 --- a/ssa.go +++ b/ssa.go @@ -72,7 +72,7 @@ func returnsInBlock(b *ssa.BasicBlock, done map[*ssa.BasicBlock]bool) (rets []*s } // BinOp returns binary operator values which are contained in the block b. -func BinOp(b *ssa.BasicBlock) ([]*ssa.BinOp) { +func BinOp(b *ssa.BasicBlock) []*ssa.BinOp { var binops []*ssa.BinOp for _, instr := range b.Instrs { if binop, ok := instr.(*ssa.BinOp); ok { @@ -81,3 +81,61 @@ func BinOp(b *ssa.BasicBlock) ([]*ssa.BinOp) { } return binops } + +// Used returns an instruction which uses the value in the block. +func Used(v ssa.Value, b *ssa.BasicBlock) ssa.Instruction { + instrs := b.Instrs[:len(b.Instrs)] + if len(instrs) == 0 || v.Referrers() == nil { + return nil + } + + for _, instr := range instrs { + if used := usedInInstr(v, instr); used != nil { + return used + } + } + + return nil +} + +func usedInInstr(v ssa.Value, instr ssa.Instruction) ssa.Instruction { + switch instr := instr.(type) { + case *ssa.MakeClosure: + return usedInClosure(v, instr) + default: + operands := instr.Operands(nil) + for _, x := range operands { + if x != nil && *x == v { + return instr + } + } + } + return nil +} + +func usedInClosure(v ssa.Value, instr *ssa.MakeClosure) ssa.Instruction { + fn, _ := instr.Fn.(*ssa.Function) + if fn == nil { + return nil + } + + var fv *ssa.FreeVar + for i := range instr.Bindings { + if instr.Bindings[i] == v { + fv = fn.FreeVars[i] + break + } + } + + if fv == nil { + return nil + } + + for _, b := range fn.Blocks { + if used := Used(fv, b); used != nil { + return used + } + } + + return nil +} diff --git a/ssa_test.go b/ssa_test.go index 4d7ccfd..9ad87a0 100644 --- a/ssa_test.go +++ b/ssa_test.go @@ -5,6 +5,9 @@ import ( "testing" "github.com/gostaticanalysis/analysisutil" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/analysistest" + "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/ssa" ) @@ -97,3 +100,27 @@ func Test_BinOp(t *testing.T) { }) } } + +func TestUsed(t *testing.T) { + a := &analysis.Analyzer{ + Name: "used", + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Run: func(pass *analysis.Pass) (interface{}, error) { + srcFuncs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs + for _, f := range srcFuncs { + if len(f.Params) != 1 { + continue + } + v := f.Params[0] + for _, b := range f.Blocks { + if analysisutil.Used(v, b) != nil { + pass.Reportf(v.Pos(), "used") + } + } + } + return nil, nil + }, + } + testdata := analysistest.TestData() + analysistest.Run(t, testdata, a, "used") +} diff --git a/testdata/src/used/used.go b/testdata/src/used/used.go new file mode 100644 index 0000000..90e60e8 --- /dev/null +++ b/testdata/src/used/used.go @@ -0,0 +1,48 @@ +package used + +var ( + N int + V interface{} +) + +func f1(v interface{}) { // want "used" + println(v) +} + +func f2(v interface{}) {} // unsed + +func f3(v interface{}) { // unsed + { + v := 100 + println(v) + } +} + +func f4(v interface{}) { // want "used" + V = v +} + +func f5(v interface{}) { // want "used" + if N == 0 { + return + } + V = v +} + +func f6(v interface{}) { // want "used" + func() { + println(v) + }() +} + +func f7(v interface{}) { // want "used" + go func() { + println(v) + }() +} + +func f8(v interface{}) { // want "used" + defer func() { + println(v) + }() +} From 5a8d89b11fd2aa07afc7c93374dc7878de989dae Mon Sep 17 00:00:00 2001 From: tenntenn Date: Thu, 1 Oct 2020 18:28:13 +0900 Subject: [PATCH 2/4] Fix badge --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index c031e16..cadea88 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # analysisutil [![godoc.org][godoc-badge]][godoc] +[![PkgGoDev](https://pkg.go.dev/badge/github.com/gostaticanalysis/analysisutil)](https://pkg.go.dev/github.com/gostaticanalysis/analysisutil) Utilities for x/tools/go/analysis package. - - -[godoc]: https://godoc.org/github.com/gostaticanalysis/analysisutil -[godoc-badge]: https://img.shields.io/badge/godoc-reference-4F73B3.svg?style=flat-square&label=%20godoc.org - From 66f19afd0480310993087d47b158f6b5de1afdba Mon Sep 17 00:00:00 2001 From: tenntenn Date: Thu, 1 Oct 2020 18:30:57 +0900 Subject: [PATCH 3/4] Remove old link --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index cadea88..d8fd3d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # analysisutil -[![godoc.org][godoc-badge]][godoc] [![PkgGoDev](https://pkg.go.dev/badge/github.com/gostaticanalysis/analysisutil)](https://pkg.go.dev/github.com/gostaticanalysis/analysisutil) Utilities for x/tools/go/analysis package. From 0e0a732ddb39525c42477785ec7a0cc2a5d1c22a Mon Sep 17 00:00:00 2001 From: tenntenn Date: Thu, 1 Oct 2020 18:39:11 +0900 Subject: [PATCH 4/4] Fix signature of Used --- ssa.go | 7 +++---- ssa_test.go | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ssa.go b/ssa.go index 3140dce..79cec17 100644 --- a/ssa.go +++ b/ssa.go @@ -82,9 +82,8 @@ func BinOp(b *ssa.BasicBlock) []*ssa.BinOp { return binops } -// Used returns an instruction which uses the value in the block. -func Used(v ssa.Value, b *ssa.BasicBlock) ssa.Instruction { - instrs := b.Instrs[:len(b.Instrs)] +// Used returns an instruction which uses the value in the instructions. +func Used(v ssa.Value, instrs []ssa.Instruction) ssa.Instruction { if len(instrs) == 0 || v.Referrers() == nil { return nil } @@ -132,7 +131,7 @@ func usedInClosure(v ssa.Value, instr *ssa.MakeClosure) ssa.Instruction { } for _, b := range fn.Blocks { - if used := Used(fv, b); used != nil { + if used := Used(fv, b.Instrs); used != nil { return used } } diff --git a/ssa_test.go b/ssa_test.go index 9ad87a0..7f7e98b 100644 --- a/ssa_test.go +++ b/ssa_test.go @@ -113,7 +113,7 @@ func TestUsed(t *testing.T) { } v := f.Params[0] for _, b := range f.Blocks { - if analysisutil.Used(v, b) != nil { + if analysisutil.Used(v, b.Instrs) != nil { pass.Reportf(v.Pos(), "used") } }