Skip to content

Instantly share code, notes, and snippets.

@Merovius

Merovius/main.go

Created Sep 11, 2014
Embed
What would you like to do?
Code für die c¼h "go tooling"
package main
import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"os"
"path/filepath"
_ "code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types"
)
type Kind uint8
const (
Concrete Kind = iota
Interface
)
type Graph []*Node
type Node struct {
Name string
K Kind
N []*Node
}
func BuildGraph(importpath string) (Graph, error) {
bpkg, err := build.Import(importpath, ".", 0)
if err != nil {
return nil, err
}
var files []*ast.File
fset := token.NewFileSet()
for _, f := range bpkg.GoFiles {
file, err := parser.ParseFile(fset, filepath.Join(bpkg.Dir, f), nil, 0)
if err != nil {
return nil, err
}
files = append(files, file)
}
tpkg, err := types.Check(importpath, fset, files)
if err != nil {
return nil, err
}
scope := tpkg.Scope()
names := scope.Names()
exportedObjects := make(map[string]*types.TypeName)
for _, n := range names {
obj := scope.Lookup(n)
if t, ok := obj.(*types.TypeName); !ok {
continue
} else {
if !t.Exported() {
continue
}
exportedObjects[n] = t
}
}
exportedIfaces := make(map[string]*types.Interface)
exportedConcretes := make(map[string]types.Type)
for n, t := range exportedObjects {
if i, ok := t.Type().Underlying().(*types.Interface); ok {
exportedIfaces[n] = i
} else {
exportedConcretes[n] = t.Type()
}
}
var g Graph
ifaces := make(map[string]*Node)
for n := range exportedIfaces {
node := &Node{n, Interface, nil}
ifaces[n] = node
g = append(g, node)
}
concr := make(map[string]*Node)
for n := range exportedConcretes {
node := &Node{n, Concrete, nil}
concr[n] = node
g = append(g, node)
}
for ni, i := range exportedIfaces {
for n, t := range exportedConcretes {
if types.Implements(t, i) || types.Implements(types.NewPointer(t), i) {
concr[n].N = append(concr[n].N, ifaces[ni])
}
}
for ni2, i2 := range exportedIfaces {
if ni == ni2 {
continue
}
if types.Implements(i2, i) {
ifaces[ni2].N = append(ifaces[ni2].N, ifaces[ni])
}
}
}
return g, nil
}
func WriteGraph(w io.Writer, g Graph) {
fmt.Fprintln(w, "digraph {")
fmt.Fprintln(w, "K=1.0")
for _, n := range g {
fmt.Fprintf(w, `"%s"`, n.Name)
if n.K == Interface {
fmt.Fprint(w, "[shape=box, style=rounded]")
} else {
fmt.Fprint(w, "[shape=box]")
}
fmt.Fprintln(w)
for _, n2 := range n.N {
fmt.Fprintf(w, "\"%s\" -> \"%s\"\n", n.Name, n2.Name)
}
}
fmt.Fprintln(w, "}")
}
func main() {
flag.Parse()
if flag.NArg() < 1 {
fmt.Fprintf(os.Stderr, "usage: %s <importpath>\n", os.Args[0])
os.Exit(1)
}
importpath := flag.Arg(0)
g, err := BuildGraph(importpath)
if err != nil {
fmt.Fprintln(os.Stderr, "Could not build graph:", err)
return
}
f, err := os.Create("graph.dot")
if err != nil {
fmt.Fprintln(os.Stderr, "Could not open graph.dot:", err)
return
}
WriteGraph(f, g)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.