Skip to content

Commit

Permalink
Merge pull request #51 from gostaticanalysis/release-v2.1.1
Browse files Browse the repository at this point in the history
Release v2.1.1
  • Loading branch information
tenntenn committed Mar 25, 2022
2 parents 94188b5 + 6c2db8e commit 79d190c
Show file tree
Hide file tree
Showing 21 changed files with 479 additions and 88 deletions.
45 changes: 24 additions & 21 deletions v2/skeleton/_template/packages/@@.Pkg@@.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
package @@.Pkg@@

import (
"flag"
"fmt"
"go/ast"
"io"
"path/filepath"

"@@.Path@@/internal"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/packages"
)

type Pass struct {
Pkg *packages.Package
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}

var Analyzer = struct {
Name string
Doc string
Flags *flag.FlagSet
Config *packages.Config
Run func(pass *Pass) error
}{
var Analyzer = &internal.Analyzer{
Name: "@@.Pkg@@",
Doc: "@@.Pkg@@ is ...",
Config: &packages.Config{
Mode: packages.NeedName | packages.NeedTypes |
packages.NeedSyntax | packages.NeedTypesInfo |
packages.NeedModule,
},
Run: run,
SSABuilderMode: 0,
Run: run,
}

func run(pass *Pass) error {
inspect := inspector.New(pass.Pkg.Syntax)
func run(pass *internal.Pass) error {
inspect := inspector.New(pass.Syntax)

nodeFilter := []ast.Node{
(*ast.Ident)(nil),
Expand All @@ -46,8 +33,8 @@ func run(pass *Pass) error {
switch n := n.(type) {
case *ast.Ident:
if n.Name == "gopher" {
pos := pass.Pkg.Fset.Position(n.Pos())
fname, err := filepath.Rel(pass.Pkg.Module.Dir, pos.Filename)
pos := pass.Fset.Position(n.Pos())
fname, err := filepath.Rel(pass.Module.Dir, pos.Filename)
if err != nil {
return
}
Expand All @@ -56,5 +43,21 @@ func run(pass *Pass) error {
}
})

// See: golang.org/x/tools/go/ssa
for _, f := range pass.SrcFuncs {
fmt.Fprintln(pass.Stdout, f)
for _, b := range f.Blocks {
fmt.Fprintf(pass.Stdout, "\tBlock %d\n", b.Index)
for _, instr := range b.Instrs {
fmt.Fprintf(pass.Stdout, "\t\t%[1]T\t%[1]v\n", instr)
for _, v := range instr.Operands(nil) {
if v != nil {
fmt.Fprintf(pass.Stdout, "\t\t\t%[1]T\t%[1]v\n", *v)
}
}
}
}
}

return nil
}
28 changes: 21 additions & 7 deletions v2/skeleton/_template/packages/@@.Pkg@@_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"testing"

"@@.Path@@"
"@@.Path@@/internal"
"github.com/tenntenn/golden"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
)

var (
Expand All @@ -23,7 +25,8 @@ func init() {
func Test(t *testing.T) {
pkgs := load(t, testdata(t), "a")
for _, pkg := range pkgs {
run(t, pkg)
prog, funcs := buildssa(t, pkg)
run(t, pkg, prog, funcs)
}
}

Expand All @@ -37,6 +40,15 @@ func load(t *testing.T, testdata string, pkgname string) []*packages.Package {
return pkgs
}

func buildssa(t *testing.T, pkg *packages.Package) (*ssa.Program, []*ssa.Function) {
t.Helper()
program, funcs, err := internal.BuildSSA(pkg, @@.Pkg@@.Analyzer.SSABuilderMode)
if err != nil {
t.Fatal("unexpected error:", err)
}
return program, funcs
}

func testdata(t *testing.T) string {
t.Helper()
dir, err := filepath.Abs("testdata")
Expand All @@ -46,13 +58,15 @@ func testdata(t *testing.T) string {
return dir
}

func run(t *testing.T, pkg *packages.Package) {
func run(t *testing.T, pkg *packages.Package, prog *ssa.Program, funcs []*ssa.Function) {
var stdin, stdout, stderr bytes.Buffer
pass := &@@.Pkg@@.Pass{
Stdin: &stdin,
Stdout: &stdout,
Stderr: &stderr,
Pkg: pkg,
pass := &internal.Pass{
Stdin: &stdin,
Stdout: &stdout,
Stderr: &stderr,
Package: pkg,
SSA: prog,
SrcFuncs: funcs,
}

if err := @@.Pkg@@.Analyzer.Run(pass); err != nil {
Expand Down
19 changes: 14 additions & 5 deletions v2/skeleton/_template/packages/cmd/@@.Pkg@@/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"fmt"
"os"

"@@.Pkg@@"
"@@.Path@@"
"@@.Path@@/internal"
"golang.org/x/tools/go/packages"
)

Expand All @@ -32,12 +33,20 @@ func run() error {
}

for _, pkg := range pkgs {
prog, srcFuncs, err := internal.Buildssa(pkg, @@.Pkg@@.Analyzer.SSABuilderMode)
if err != nil {
return err
}

pass := &@@.Pkg@@.Pass{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Pkg: pkg,
Package: pkg,
SSA: prog,
SrcFuncs: srcFuncs,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}

if err := @@.Pkg@@.Analyzer.Run(pass); err != nil {
return err
}
Expand Down
27 changes: 27 additions & 0 deletions v2/skeleton/_template/packages/internal/analyzer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package internal

import (
"flag"
"io"

"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
)

type Pass struct {
*packages.Package
SSA *ssa.Program
SrcFuncs []*ssa.Function
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}

type Analyzer struct {
Name string
Doc string
Flags *flag.FlagSet
Config *packages.Config
SSABuilderMode ssa.BuilderMode
Run func(pass *Pass) error
}
78 changes: 78 additions & 0 deletions v2/skeleton/_template/packages/internal/buildssa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package internal

import (
"go/ast"
"go/types"

"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
)

// copy from golang.org/x/tools/analysis/passes/buildssa
func BuildSSA(pkg *packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Function, error) {

prog := ssa.NewProgram(pkg.Fset, mode)

// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pkg.Types.Imports())

// Create and build the primary package.
ssapkg := prog.CreatePackage(pkg.Types, pkg.Syntax, pkg.TypesInfo, false)
ssapkg.Build()

// Compute list of source functions, including literals,
// in source order.
var funcs []*ssa.Function
for _, f := range pkg.Syntax {
for _, decl := range f.Decls {
if fdecl, ok := decl.(*ast.FuncDecl); ok {

// SSA will not build a Function
// for a FuncDecl named blank.
// That's arguably too strict but
// relaxing it would break uniqueness of
// names of package members.
if fdecl.Name.Name == "_" {
continue
}

// (init functions have distinct Func
// objects named "init" and distinct
// ssa.Functions named "init#1", ...)

fn := pkg.TypesInfo.Defs[fdecl.Name].(*types.Func)
if fn == nil {
panic(fn)
}

f := ssapkg.Prog.FuncValue(fn)
if f == nil {
panic(fn)
}

var addAnons func(f *ssa.Function)
addAnons = func(f *ssa.Function) {
funcs = append(funcs, f)
for _, anon := range f.AnonFuncs {
addAnons(anon)
}
}
addAnons(f)
}
}
}

return prog, funcs, nil
}
8 changes: 6 additions & 2 deletions v2/skeleton/_template/packages/testdata/a-stdout.golden
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
@@ if eq .Kind "packages" -@@
a.go:5:6 identifier is gopher
a.go:6:8 identifier is gopher
@@ end -@@
a.f
Block 0
*ssa.Call print(0:int)
*ssa.Builtin builtin print
*ssa.Const 0:int
*ssa.Return return
4 changes: 2 additions & 2 deletions v2/skeleton/_template/ssa/@@.Pkg@@.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
for _, b := range f.Blocks {
fmt.Printf("\tBlock %d\n", b.Index)
for _, instr := range b.Instrs {
fmt.Printf("\t\t%[1]T\t%[1]v(%[1]p)\n", instr)
fmt.Printf("\t\t%[1]T\t%[1]v\n", instr)
for _, v := range instr.Operands(nil) {
if v != nil {
fmt.Printf("\t\t\t%[1]T\t%[1]v(%[1]p)\n", *v)
fmt.Printf("\t\t\t%[1]T\t%[1]v\n", *v)
}
}
}
Expand Down
Loading

0 comments on commit 79d190c

Please sign in to comment.