mirror of
https://github.com/golang/go
synced 2024-11-18 14:54:40 -07: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:
parent
cc1e254c18
commit
845abca4e3
27
go/types/typeutil/imports.go
Normal file
27
go/types/typeutil/imports.go
Normal 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
|
||||
}
|
75
go/types/typeutil/imports_test.go
Normal file
75
go/types/typeutil/imports_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user