1
0
mirror of https://github.com/golang/go synced 2024-09-30 18:18:32 -06:00

go/types/typeutil: Dependencies utility returns transitive closure of import graph.

(Used by forthcoming 'singlefile' command.)

+ Test.

LGTM=bwkster, gri
R=gri, bwkster
CC=golang-codereviews
https://golang.org/cl/130100043
This commit is contained in:
Alan Donovan 2014-08-18 22:31:56 -04:00
parent cc1e254c18
commit 845abca4e3
2 changed files with 102 additions and 0 deletions

View File

@ -0,0 +1,27 @@
package typeutil
import "code.google.com/p/go.tools/go/types"
// Dependencies returns all dependencies of the specified packages.
//
// Dependent packages appear in topological order: if package P imports
// package Q, Q appears earlier than P in the result.
// The algorithm follows import statements in the order they
// appear in the source code, so the result is a total order.
//
func Dependencies(pkgs ...*types.Package) []*types.Package {
var result []*types.Package
seen := make(map[*types.Package]bool)
var visit func(pkgs []*types.Package)
visit = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !seen[p] {
seen[p] = true
visit(p.Imports())
result = append(result, p)
}
}
}
visit(pkgs)
return result
}

View File

@ -0,0 +1,75 @@
package typeutil_test
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"testing"
"code.google.com/p/go.tools/go/types"
"code.google.com/p/go.tools/go/types/typeutil"
)
func TestDependencies(t *testing.T) {
packages := make(map[string]*types.Package)
conf := types.Config{
Packages: packages,
Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
return packages[path], nil
},
}
fset := token.NewFileSet()
// All edges go to the right.
// /--D--B--A
// F \_C_/
// \__E_/
for i, content := range []string{
`package A`,
`package C; import (_ "A")`,
`package B; import (_ "A")`,
`package E; import (_ "C")`,
`package D; import (_ "B"; _ "C")`,
`package F; import (_ "D"; _ "E")`,
} {
f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
if err != nil {
t.Fatal(err)
}
pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
if err != nil {
t.Fatal(err)
}
packages[pkg.Path()] = pkg
}
for _, test := range []struct {
roots, want string
}{
{"A", "A"},
{"B", "AB"},
{"C", "AC"},
{"D", "ABCD"},
{"E", "ACE"},
{"F", "ABCDEF"},
{"BE", "ABCE"},
{"EB", "ACEB"},
{"DE", "ABCDE"},
{"ED", "ACEBD"},
{"EF", "ACEBDF"},
} {
var pkgs []*types.Package
for _, r := range test.roots {
pkgs = append(pkgs, conf.Packages[string(r)])
}
var got string
for _, p := range typeutil.Dependencies(pkgs...) {
got += p.Path()
}
if got != test.want {
t.Errorf("Dependencies(%q) = %q, want %q", test.roots, got, test.want)
}
}
}