mirror of
https://github.com/golang/go
synced 2024-11-18 15:44:41 -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