From 542ffc7e75cf45ac7ad456664a710cd96338fda6 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Tue, 29 Dec 2015 13:06:30 -0500 Subject: [PATCH] tools: switch to standard go/types at tip A few files have been forked and tagged "go1.5,!go1.6" to work around minor API changes between the two types packages: - constant.Value.String() in oracle/describe.go and its tests; - constant.ToInt must now be called before constant.Int64Val. - types.Config{Importer: importer.Default()} in a number of places - go/types/typeutil/import_test.go uses lowercase names to avoid 'import "C"'. Files in go/types/typesutil, missing from my previous CL, have been tagged !go1.5; these files will be deleted in February. All affected packages were tested using 1.4.1, 1.5, and ~1.6 (tip). Change-Id: Iec7fd370e1434508149b378438fb37f65b8d2ba8 Reviewed-on: https://go-review.googlesource.com/18207 Reviewed-by: Robert Griesemer --- cmd/bundle/main.go | 2 +- cmd/ssadump/main.go | 2 +- cmd/stringer/stringer.go | 10 +- cmd/vet/copylock.go | 3 +- cmd/vet/example.go | 3 +- cmd/vet/main.go | 4 +- cmd/vet/nilfunc.go | 3 +- cmd/vet/print.go | 5 +- cmd/vet/shadow.go | 3 +- cmd/vet/shift.go | 5 +- cmd/vet/types.go | 12 +- cmd/vet/unsafeptr.go | 3 +- cmd/vet/unused.go | 3 +- go/callgraph/cha/cha.go | 3 +- go/callgraph/cha/cha_test.go | 2 +- go/callgraph/rta/rta.go | 2 +- go/callgraph/rta/rta_test.go | 2 +- go/loader/loader.go | 13 +- go/loader/stdlib_test.go | 2 +- go/pointer/analysis.go | 2 +- go/pointer/constraint.go | 4 +- go/pointer/gen.go | 2 +- go/pointer/hvn.go | 2 +- go/pointer/intrinsics.go | 2 +- go/pointer/labels.go | 2 +- go/pointer/pointer_test.go | 2 +- go/pointer/reflect.go | 4 +- go/pointer/solve.go | 3 +- go/pointer/util.go | 2 +- go/ssa/builder.go | 7 +- go/ssa/builder_test.go | 9 +- go/ssa/const.go | 13 +- go/ssa/const15.go | 171 +++++ go/ssa/create.go | 2 +- go/ssa/emit.go | 3 +- go/ssa/example_test.go | 8 +- go/ssa/func.go | 3 +- go/ssa/interp/external.go | 2 +- go/ssa/interp/interp.go | 2 +- go/ssa/interp/interp_test.go | 2 +- go/ssa/interp/map.go | 2 +- go/ssa/interp/ops.go | 4 +- go/ssa/interp/reflect.go | 2 +- go/ssa/interp/value.go | 2 +- go/ssa/lift.go | 3 +- go/ssa/lvalue.go | 3 +- go/ssa/methods.go | 3 +- go/ssa/print.go | 2 +- go/ssa/sanity.go | 3 +- go/ssa/source.go | 3 +- go/ssa/source_test.go | 4 +- go/ssa/ssa.go | 4 +- go/ssa/ssautil/load.go | 2 +- go/ssa/ssautil/load_test.go | 7 +- go/ssa/ssautil/switch.go | 2 +- go/ssa/testmain.go | 5 +- go/ssa/util.go | 2 +- go/ssa/wrappers.go | 2 +- go/types/typeutil/example_test.go | 7 +- go/types/typeutil/imports.go | 4 +- go/types/typeutil/imports14.go | 33 + go/types/typeutil/imports14_test.go | 81 +++ go/types/typeutil/imports_test.go | 49 +- go/types/typeutil/map.go | 5 +- go/types/typeutil/map14.go | 316 +++++++++ go/types/typeutil/map14_test.go | 176 +++++ go/types/typeutil/map_test.go | 4 +- go/types/typeutil/methodsetcache.go | 5 +- go/types/typeutil/methodsetcache14.go | 75 +++ go/types/typeutil/ui.go | 4 +- go/types/typeutil/ui14.go | 40 ++ godoc/analysis/analysis.go | 4 +- godoc/analysis/callgraph.go | 2 +- godoc/analysis/implements.go | 2 +- godoc/analysis/peers.go | 2 +- godoc/analysis/typeinfo.go | 2 +- oracle/callees.go | 2 +- oracle/definition.go | 2 +- oracle/describe.go | 6 +- oracle/describe14.go | 17 +- oracle/describe15.go | 780 +++++++++++++++++++++++ oracle/freevars.go | 2 +- oracle/implements.go | 2 +- oracle/oracle.go | 2 +- oracle/peers.go | 2 +- oracle/pointsto.go | 2 +- oracle/referrers.go | 2 +- oracle/testdata/src/describe/main.golden | 8 +- oracle/whicherrs.go | 2 +- refactor/eg/eg.go | 3 +- refactor/eg/eg_test.go | 4 +- refactor/eg/match.go | 2 +- refactor/eg/rewrite.go | 2 +- refactor/rename/check.go | 2 +- refactor/rename/rename.go | 2 +- refactor/rename/spec.go | 2 +- refactor/rename/util.go | 2 +- refactor/satisfy/find.go | 2 +- 98 files changed, 1860 insertions(+), 185 deletions(-) create mode 100644 go/ssa/const15.go create mode 100644 go/types/typeutil/imports14.go create mode 100644 go/types/typeutil/imports14_test.go create mode 100644 go/types/typeutil/map14.go create mode 100644 go/types/typeutil/map14_test.go create mode 100644 go/types/typeutil/methodsetcache14.go create mode 100644 go/types/typeutil/ui14.go create mode 100644 oracle/describe15.go diff --git a/cmd/bundle/main.go b/cmd/bundle/main.go index 69671967..f0051fef 100644 --- a/cmd/bundle/main.go +++ b/cmd/bundle/main.go @@ -44,6 +44,7 @@ import ( "go/format" "go/parser" "go/token" + "go/types" "io" "log" "os" @@ -51,7 +52,6 @@ import ( "strings" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" ) func main() { diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go index dac10987..bdc4d0a2 100644 --- a/cmd/ssadump/main.go +++ b/cmd/ssadump/main.go @@ -11,6 +11,7 @@ import ( "flag" "fmt" "go/build" + "go/types" "os" "runtime" "runtime/pprof" @@ -20,7 +21,6 @@ import ( "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) var ( diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go index 38c9a268..4afecd6f 100644 --- a/cmd/stringer/stringer.go +++ b/cmd/stringer/stringer.go @@ -64,20 +64,18 @@ import ( "fmt" "go/ast" "go/build" + exact "go/constant" "go/format" + "go/importer" "go/parser" "go/token" + "go/types" "io/ioutil" "log" "os" "path/filepath" "sort" "strings" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" - - _ "golang.org/x/tools/go/gcimporter" ) var ( @@ -260,7 +258,7 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac // check type-checks the package. The package must be OK to proceed. func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) { pkg.defs = make(map[*ast.Ident]types.Object) - config := types.Config{FakeImportC: true} + config := types.Config{Importer: importer.Default(), FakeImportC: true} info := &types.Info{ Defs: pkg.defs, } diff --git a/cmd/vet/copylock.go b/cmd/vet/copylock.go index e8a6820f..95cecc79 100644 --- a/cmd/vet/copylock.go +++ b/cmd/vet/copylock.go @@ -11,8 +11,7 @@ import ( "fmt" "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) func init() { diff --git a/cmd/vet/example.go b/cmd/vet/example.go index 585d3884..797c3cee 100644 --- a/cmd/vet/example.go +++ b/cmd/vet/example.go @@ -6,11 +6,10 @@ package main import ( "go/ast" + "go/types" "strings" "unicode" "unicode/utf8" - - "golang.org/x/tools/go/types" ) func init() { diff --git a/cmd/vet/main.go b/cmd/vet/main.go index c86e13c4..8bef050d 100644 --- a/cmd/vet/main.go +++ b/cmd/vet/main.go @@ -18,14 +18,12 @@ import ( "go/parser" "go/printer" "go/token" + "go/types" "io/ioutil" "os" "path/filepath" "strconv" "strings" - - _ "golang.org/x/tools/go/gcimporter" - "golang.org/x/tools/go/types" ) var ( diff --git a/cmd/vet/nilfunc.go b/cmd/vet/nilfunc.go index fa1bac7e..bfe05e33 100644 --- a/cmd/vet/nilfunc.go +++ b/cmd/vet/nilfunc.go @@ -12,8 +12,7 @@ package main import ( "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) func init() { diff --git a/cmd/vet/print.go b/cmd/vet/print.go index b20d935e..42c86a51 100644 --- a/cmd/vet/print.go +++ b/cmd/vet/print.go @@ -10,13 +10,12 @@ import ( "bytes" "flag" "go/ast" + exact "go/constant" "go/token" + "go/types" "strconv" "strings" "unicode/utf8" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") diff --git a/cmd/vet/shadow.go b/cmd/vet/shadow.go index fa680a03..2149e70c 100644 --- a/cmd/vet/shadow.go +++ b/cmd/vet/shadow.go @@ -34,8 +34,7 @@ import ( "flag" "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy") diff --git a/cmd/vet/shift.go b/cmd/vet/shift.go index 2385c23f..e70bfcfb 100644 --- a/cmd/vet/shift.go +++ b/cmd/vet/shift.go @@ -10,10 +10,9 @@ package main import ( "go/ast" + exact "go/constant" "go/token" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" + "go/types" ) func init() { diff --git a/cmd/vet/types.go b/cmd/vet/types.go index 084be85d..46478237 100644 --- a/cmd/vet/types.go +++ b/cmd/vet/types.go @@ -8,9 +8,9 @@ package main import ( "go/ast" + "go/importer" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) // imports is the canonical map of imported packages we need for typechecking. @@ -18,6 +18,8 @@ import ( var imports = make(map[string]*types.Package) var ( + defaultImporter = importer.Default() + errorType *types.Interface stringerType *types.Interface // possibly nil formatterType *types.Interface // possibly nil @@ -39,7 +41,7 @@ func init() { // path.name, and adds the respective package to the imports map // as a side effect. In case of an error, importType returns nil. func importType(path, name string) types.Type { - pkg, err := types.DefaultImport(imports, path) + pkg, err := defaultImporter.Import(path) if err != nil { // This can happen if the package at path hasn't been compiled yet. warnf("import failed: %v", err) @@ -59,9 +61,7 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { pkg.spans = make(map[types.Object]Span) pkg.types = make(map[ast.Expr]types.TypeAndValue) config := types.Config{ - // We provide the same packages map for all imports to ensure - // that everybody sees identical packages for the given paths. - Packages: imports, + Importer: defaultImporter, // By providing a Config with our own error function, it will continue // past the first error. There is no need for that function to do anything. Error: func(error) {}, diff --git a/cmd/vet/unsafeptr.go b/cmd/vet/unsafeptr.go index ca15f725..9ca27dce 100644 --- a/cmd/vet/unsafeptr.go +++ b/cmd/vet/unsafeptr.go @@ -9,8 +9,7 @@ package main import ( "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) func init() { diff --git a/cmd/vet/unused.go b/cmd/vet/unused.go index b9d7b65f..df2317a4 100644 --- a/cmd/vet/unused.go +++ b/cmd/vet/unused.go @@ -11,9 +11,8 @@ import ( "flag" "go/ast" "go/token" + "go/types" "strings" - - "golang.org/x/tools/go/types" ) var unusedFuncsFlag = flag.String("unusedfuncs", diff --git a/go/callgraph/cha/cha.go b/go/callgraph/cha/cha.go index 962f9193..e016649b 100644 --- a/go/callgraph/cha/cha.go +++ b/go/callgraph/cha/cha.go @@ -26,10 +26,11 @@ package cha // import "golang.org/x/tools/go/callgraph/cha" import ( + "go/types" + "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/callgraph/cha/cha_test.go b/go/callgraph/cha/cha_test.go index a5580743..332758cf 100644 --- a/go/callgraph/cha/cha_test.go +++ b/go/callgraph/cha/cha_test.go @@ -16,6 +16,7 @@ import ( "go/ast" "go/parser" "go/token" + "go/types" "io/ioutil" "sort" "strings" @@ -25,7 +26,6 @@ import ( "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) var inputs = []string{ diff --git a/go/callgraph/rta/rta.go b/go/callgraph/rta/rta.go index 0d0b0d30..7c9379d6 100644 --- a/go/callgraph/rta/rta.go +++ b/go/callgraph/rta/rta.go @@ -50,10 +50,10 @@ package rta // import "golang.org/x/tools/go/callgraph/rta" import ( "fmt" + "go/types" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/callgraph/rta/rta_test.go b/go/callgraph/rta/rta_test.go index d37cc5c0..50465212 100644 --- a/go/callgraph/rta/rta_test.go +++ b/go/callgraph/rta/rta_test.go @@ -16,6 +16,7 @@ import ( "go/ast" "go/parser" "go/token" + "go/types" "io/ioutil" "sort" "strings" @@ -26,7 +27,6 @@ import ( "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) var inputs = []string{ diff --git a/go/loader/loader.go b/go/loader/loader.go index ecdc85d1..41d5957d 100644 --- a/go/loader/loader.go +++ b/go/loader/loader.go @@ -15,6 +15,7 @@ import ( "go/build" "go/parser" "go/token" + "go/types" "os" "sort" "strings" @@ -23,7 +24,6 @@ import ( "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/buildutil" - "golang.org/x/tools/go/types" ) const trace = false // show timing info for type-checking @@ -1005,9 +1005,7 @@ func (imp *importer) newPackageInfo(path, dir string) *PackageInfo { if f := imp.conf.TypeCheckFuncBodies; f != nil { tc.IgnoreFuncBodies = !f(path) } - tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) { - return imp.doImport(info, to) - } + tc.Importer = closure{imp, info} tc.Error = info.appendError // appendError wraps the user's Error function info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) @@ -1016,3 +1014,10 @@ func (imp *importer) newPackageInfo(path, dir string) *PackageInfo { imp.progMu.Unlock() return info } + +type closure struct { + imp *importer + info *PackageInfo +} + +func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) } diff --git a/go/loader/stdlib_test.go b/go/loader/stdlib_test.go index 23f6f017..812a4a69 100644 --- a/go/loader/stdlib_test.go +++ b/go/loader/stdlib_test.go @@ -16,6 +16,7 @@ import ( "go/ast" "go/build" "go/token" + "go/types" "io/ioutil" "path/filepath" "runtime" @@ -25,7 +26,6 @@ import ( "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" ) func TestStdlib(t *testing.T) { diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go index 4a7439e0..9f3476ce 100644 --- a/go/pointer/analysis.go +++ b/go/pointer/analysis.go @@ -11,6 +11,7 @@ package pointer import ( "fmt" "go/token" + "go/types" "io" "os" "reflect" @@ -20,7 +21,6 @@ import ( "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/pointer/constraint.go b/go/pointer/constraint.go index 5b9265a5..ea442873 100644 --- a/go/pointer/constraint.go +++ b/go/pointer/constraint.go @@ -6,9 +6,7 @@ package pointer -import ( - "golang.org/x/tools/go/types" -) +import "go/types" type constraint interface { // For a complex constraint, returns the nodeid of the pointer diff --git a/go/pointer/gen.go b/go/pointer/gen.go index 9d550a70..405a63b8 100644 --- a/go/pointer/gen.go +++ b/go/pointer/gen.go @@ -15,10 +15,10 @@ package pointer import ( "fmt" "go/token" + "go/types" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) var ( diff --git a/go/pointer/hvn.go b/go/pointer/hvn.go index c1445597..e550bc9c 100644 --- a/go/pointer/hvn.go +++ b/go/pointer/hvn.go @@ -165,11 +165,11 @@ package pointer import ( "fmt" + "go/types" "io" "reflect" "golang.org/x/tools/container/intsets" - "golang.org/x/tools/go/types" ) // A peLabel is a pointer-equivalence label: two nodes with the same diff --git a/go/pointer/intrinsics.go b/go/pointer/intrinsics.go index 69da3779..fbfb36d9 100644 --- a/go/pointer/intrinsics.go +++ b/go/pointer/intrinsics.go @@ -19,9 +19,9 @@ package pointer import ( "fmt" + "go/types" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // Instances of 'intrinsic' generate analysis constraints for calls to diff --git a/go/pointer/labels.go b/go/pointer/labels.go index 47092722..bfe60d2f 100644 --- a/go/pointer/labels.go +++ b/go/pointer/labels.go @@ -9,10 +9,10 @@ package pointer import ( "fmt" "go/token" + "go/types" "strings" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // A Label is an entity that may be pointed to by a pointer, map, diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go index ddfacf71..52af485b 100644 --- a/go/pointer/pointer_test.go +++ b/go/pointer/pointer_test.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "go/token" + "go/types" "io/ioutil" "os" "regexp" @@ -31,7 +32,6 @@ import ( "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/pointer/reflect.go b/go/pointer/reflect.go index a0bfda6e..bdb22cf9 100644 --- a/go/pointer/reflect.go +++ b/go/pointer/reflect.go @@ -32,11 +32,11 @@ package pointer import ( "fmt" + exact "go/constant" + "go/types" "reflect" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) func init() { diff --git a/go/pointer/solve.go b/go/pointer/solve.go index 8cff32f3..3c606854 100644 --- a/go/pointer/solve.go +++ b/go/pointer/solve.go @@ -11,8 +11,7 @@ package pointer import ( "fmt" - - "golang.org/x/tools/go/types" + "go/types" ) type solverState struct { diff --git a/go/pointer/util.go b/go/pointer/util.go index 7a431870..4d2fa74f 100644 --- a/go/pointer/util.go +++ b/go/pointer/util.go @@ -9,6 +9,7 @@ package pointer import ( "bytes" "fmt" + "go/types" "log" "os" "os/exec" @@ -16,7 +17,6 @@ import ( "time" "golang.org/x/tools/container/intsets" - "golang.org/x/tools/go/types" ) // CanPoint reports whether the type T is pointerlike, diff --git a/go/ssa/builder.go b/go/ssa/builder.go index 9a0a4749..4707ebeb 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -34,12 +34,11 @@ package ssa import ( "fmt" "go/ast" + exact "go/constant" "go/token" + "go/types" "os" "sync" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) type opaqueType struct { @@ -244,7 +243,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ } if m, ok := m.(*Const); ok { // treat make([]T, n, m) as new([m]T)[:n] - cap, _ := exact.Int64Val(m.Value) + cap := m.Int64() at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap) alloc := emitNew(fn, at, pos) alloc.Comment = "makeslice" diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go index f0556258..fdb58ff9 100644 --- a/go/ssa/builder_test.go +++ b/go/ssa/builder_test.go @@ -9,8 +9,10 @@ package ssa_test import ( "bytes" "go/ast" + "go/importer" "go/parser" "go/token" + "go/types" "reflect" "sort" "strings" @@ -19,9 +21,6 @@ import ( "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" - - _ "golang.org/x/tools/go/gcimporter" ) func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } @@ -59,7 +58,7 @@ func main() { // Build an SSA program from the parsed file. // Load its dependencies from gc binary export data. - mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset, + mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions) if err != nil { t.Error(err) @@ -227,7 +226,7 @@ func TestRuntimeTypes(t *testing.T) { // Create a single-file main package. // Load dependencies from gc binary export data. - ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, + ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions) if err != nil { t.Errorf("test %q: %s", test.input[:15], err) diff --git a/go/ssa/const.go b/go/ssa/const.go index 3a1746d8..0690463d 100644 --- a/go/ssa/const.go +++ b/go/ssa/const.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build go1.5 +// +build go1.6 package ssa @@ -10,11 +10,10 @@ package ssa import ( "fmt" + exact "go/constant" "go/token" + "go/types" "strconv" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) // NewConst returns a new constant of the specified value and type. @@ -118,11 +117,13 @@ func (c *Const) IsNil() bool { return c.Value == nil } +// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp. + // Int64 returns the numeric value of this constant truncated to fit // a signed 64-bit integer. // func (c *Const) Int64() int64 { - switch x := c.Value; x.Kind() { + switch x := exact.ToInt(c.Value); x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i @@ -139,7 +140,7 @@ func (c *Const) Int64() int64 { // an unsigned 64-bit integer. // func (c *Const) Uint64() uint64 { - switch x := c.Value; x.Kind() { + switch x := exact.ToInt(c.Value); x.Kind() { case exact.Int: if u, ok := exact.Uint64Val(x); ok { return u diff --git a/go/ssa/const15.go b/go/ssa/const15.go new file mode 100644 index 00000000..a42b255a --- /dev/null +++ b/go/ssa/const15.go @@ -0,0 +1,171 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5,!go1.6 + +package ssa + +// This file defines the Const SSA value type. + +import ( + "fmt" + exact "go/constant" + "go/token" + "go/types" + "strconv" +) + +// NewConst returns a new constant of the specified value and type. +// val must be valid according to the specification of Const.Value. +// +func NewConst(val exact.Value, typ types.Type) *Const { + return &Const{typ, val} +} + +// intConst returns an 'int' constant that evaluates to i. +// (i is an int64 in case the host is narrower than the target.) +func intConst(i int64) *Const { + return NewConst(exact.MakeInt64(i), tInt) +} + +// nilConst returns a nil constant of the specified type, which may +// be any reference type, including interfaces. +// +func nilConst(typ types.Type) *Const { + return NewConst(nil, typ) +} + +// stringConst returns a 'string' constant that evaluates to s. +func stringConst(s string) *Const { + return NewConst(exact.MakeString(s), tString) +} + +// zeroConst returns a new "zero" constant of the specified type, +// which must not be an array or struct type: the zero values of +// aggregates are well-defined but cannot be represented by Const. +// +func zeroConst(t types.Type) *Const { + switch t := t.(type) { + case *types.Basic: + switch { + case t.Info()&types.IsBoolean != 0: + return NewConst(exact.MakeBool(false), t) + case t.Info()&types.IsNumeric != 0: + return NewConst(exact.MakeInt64(0), t) + case t.Info()&types.IsString != 0: + return NewConst(exact.MakeString(""), t) + case t.Kind() == types.UnsafePointer: + fallthrough + case t.Kind() == types.UntypedNil: + return nilConst(t) + default: + panic(fmt.Sprint("zeroConst for unexpected type:", t)) + } + case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: + return nilConst(t) + case *types.Named: + return NewConst(zeroConst(t.Underlying()).Value, t) + case *types.Array, *types.Struct, *types.Tuple: + panic(fmt.Sprint("zeroConst applied to aggregate:", t)) + } + panic(fmt.Sprint("zeroConst: unexpected ", t)) +} + +func (c *Const) RelString(from *types.Package) string { + var s string + if c.Value == nil { + s = "nil" + } else if c.Value.Kind() == exact.String { + s = exact.StringVal(c.Value) + const max = 20 + // TODO(adonovan): don't cut a rune in half. + if len(s) > max { + s = s[:max-3] + "..." // abbreviate + } + s = strconv.Quote(s) + } else { + s = c.Value.String() + } + return s + ":" + relType(c.Type(), from) +} + +func (c *Const) Name() string { + return c.RelString(nil) +} + +func (c *Const) String() string { + return c.Name() +} + +func (c *Const) Type() types.Type { + return c.typ +} + +func (c *Const) Referrers() *[]Instruction { + return nil +} + +func (c *Const) Parent() *Function { return nil } + +func (c *Const) Pos() token.Pos { + return token.NoPos +} + +// IsNil returns true if this constant represents a typed or untyped nil value. +func (c *Const) IsNil() bool { + return c.Value == nil +} + +// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp. + +// Int64 returns the numeric value of this constant truncated to fit +// a signed 64-bit integer. +// +func (c *Const) Int64() int64 { + switch x := c.Value; x.Kind() { + case exact.Int: + if i, ok := exact.Int64Val(x); ok { + return i + } + return 0 + case exact.Float: + f, _ := exact.Float64Val(x) + return int64(f) + } + panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) +} + +// Uint64 returns the numeric value of this constant truncated to fit +// an unsigned 64-bit integer. +// +func (c *Const) Uint64() uint64 { + switch x := c.Value; x.Kind() { + case exact.Int: + if u, ok := exact.Uint64Val(x); ok { + return u + } + return 0 + case exact.Float: + f, _ := exact.Float64Val(x) + return uint64(f) + } + panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) +} + +// Float64 returns the numeric value of this constant truncated to fit +// a float64. +// +func (c *Const) Float64() float64 { + f, _ := exact.Float64Val(c.Value) + return f +} + +// Complex128 returns the complex value of this constant truncated to +// fit a complex128. +// +func (c *Const) Complex128() complex128 { + re, _ := exact.Float64Val(exact.Real(c.Value)) + im, _ := exact.Float64Val(exact.Imag(c.Value)) + return complex(re, im) +} diff --git a/go/ssa/create.go b/go/ssa/create.go index b49a8bee..372d1c72 100644 --- a/go/ssa/create.go +++ b/go/ssa/create.go @@ -13,10 +13,10 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "os" "sync" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/ssa/emit.go b/go/ssa/emit.go index dd4ff937..238c0706 100644 --- a/go/ssa/emit.go +++ b/go/ssa/emit.go @@ -12,8 +12,7 @@ import ( "fmt" "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) // emitNew emits to f a new (heap Alloc) instruction allocating an diff --git a/go/ssa/example_test.go b/go/ssa/example_test.go index f25e31d0..718817cc 100644 --- a/go/ssa/example_test.go +++ b/go/ssa/example_test.go @@ -8,16 +8,16 @@ package ssa_test import ( "fmt" - "os" - "go/ast" + "go/importer" "go/parser" "go/token" + "go/types" + "os" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) const hello = ` @@ -66,7 +66,7 @@ func ExampleBuildPackage() { // Type-check the package, load dependencies. // Create and build the SSA program. hello, _, err := ssautil.BuildPackage( - new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions) + &types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions) if err != nil { fmt.Print(err) // type error in some package return diff --git a/go/ssa/func.go b/go/ssa/func.go index 7d9e8f9a..88052c32 100644 --- a/go/ssa/func.go +++ b/go/ssa/func.go @@ -13,11 +13,10 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "io" "os" "strings" - - "golang.org/x/tools/go/types" ) // addEdge adds a control-flow graph edge from from to to. diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go index 2425163d..8ae81b2a 100644 --- a/go/ssa/interp/external.go +++ b/go/ssa/interp/external.go @@ -10,6 +10,7 @@ package interp // external or because they use "unsafe" or "reflect" operations. import ( + "go/types" "math" "os" "runtime" @@ -19,7 +20,6 @@ import ( "unsafe" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) type externalFn func(fr *frame, args []value) value diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go index 87e6b819..b855645f 100644 --- a/go/ssa/interp/interp.go +++ b/go/ssa/interp/interp.go @@ -49,12 +49,12 @@ package interp // import "golang.org/x/tools/go/ssa/interp" import ( "fmt" "go/token" + "go/types" "os" "reflect" "runtime" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) type continuation int diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index bcdd81cb..5163816a 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -12,6 +12,7 @@ import ( "bytes" "fmt" "go/build" + "go/types" "os" "path/filepath" "strings" @@ -22,7 +23,6 @@ import ( "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) // Each line contains a space-separated list of $GOROOT/test/ diff --git a/go/ssa/interp/map.go b/go/ssa/interp/map.go index 6d4c3ae4..4c092b3e 100644 --- a/go/ssa/interp/map.go +++ b/go/ssa/interp/map.go @@ -14,7 +14,7 @@ package interp // concurrent map access. import ( - "golang.org/x/tools/go/types" + "go/types" ) type hashable interface { diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go index a76b579b..c7a0a406 100644 --- a/go/ssa/interp/ops.go +++ b/go/ssa/interp/ops.go @@ -9,14 +9,14 @@ package interp import ( "bytes" "fmt" + exact "go/constant" "go/token" + "go/types" "strings" "sync" "unsafe" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // If the target program panics, the interpreter panics with this type. diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go index 01cd00b9..48bb9112 100644 --- a/go/ssa/interp/reflect.go +++ b/go/ssa/interp/reflect.go @@ -15,11 +15,11 @@ package interp import ( "fmt" "go/token" + "go/types" "reflect" "unsafe" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) type opaqueType struct { diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go index 37be184c..2194b013 100644 --- a/go/ssa/interp/value.go +++ b/go/ssa/interp/value.go @@ -38,6 +38,7 @@ package interp import ( "bytes" "fmt" + "go/types" "io" "reflect" "strings" @@ -45,7 +46,6 @@ import ( "unsafe" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/ssa/lift.go b/go/ssa/lift.go index d0a145dd..722d086f 100644 --- a/go/ssa/lift.go +++ b/go/ssa/lift.go @@ -46,10 +46,9 @@ package ssa import ( "fmt" "go/token" + "go/types" "math/big" "os" - - "golang.org/x/tools/go/types" ) // If true, perform sanity checking and show diagnostic information at diff --git a/go/ssa/lvalue.go b/go/ssa/lvalue.go index 657204dd..85e090f4 100644 --- a/go/ssa/lvalue.go +++ b/go/ssa/lvalue.go @@ -12,8 +12,7 @@ package ssa import ( "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) // An lvalue represents an assignable location that may appear on the diff --git a/go/ssa/methods.go b/go/ssa/methods.go index ba36bbff..7d1fb42b 100644 --- a/go/ssa/methods.go +++ b/go/ssa/methods.go @@ -10,8 +10,7 @@ package ssa import ( "fmt" - - "golang.org/x/tools/go/types" + "go/types" ) // MethodValue returns the Function implementing method sel, building diff --git a/go/ssa/print.go b/go/ssa/print.go index c3896169..55c92660 100644 --- a/go/ssa/print.go +++ b/go/ssa/print.go @@ -12,11 +12,11 @@ package ssa import ( "bytes" "fmt" + "go/types" "io" "reflect" "sort" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go index 01e97e52..4babb375 100644 --- a/go/ssa/sanity.go +++ b/go/ssa/sanity.go @@ -11,11 +11,10 @@ package ssa import ( "fmt" + "go/types" "io" "os" "strings" - - "golang.org/x/tools/go/types" ) type sanity struct { diff --git a/go/ssa/source.go b/go/ssa/source.go index 0926c051..3a6f0392 100644 --- a/go/ssa/source.go +++ b/go/ssa/source.go @@ -15,8 +15,7 @@ package ssa import ( "go/ast" "go/token" - - "golang.org/x/tools/go/types" + "go/types" ) // EnclosingFunction returns the function that contains the syntax diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go index 91469074..3fd7ac4a 100644 --- a/go/ssa/source_test.go +++ b/go/ssa/source_test.go @@ -11,8 +11,10 @@ package ssa_test import ( "fmt" "go/ast" + exact "go/constant" "go/parser" "go/token" + "go/types" "os" "regexp" "runtime" @@ -20,11 +22,9 @@ import ( "testing" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) func TestObjValueLookup(t *testing.T) { diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go index 513f87fc..c547af8a 100644 --- a/go/ssa/ssa.go +++ b/go/ssa/ssa.go @@ -12,11 +12,11 @@ package ssa import ( "fmt" "go/ast" + exact "go/constant" "go/token" + "go/types" "sync" - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/ssa/ssautil/load.go b/go/ssa/ssautil/load.go index 4061f49f..7c578386 100644 --- a/go/ssa/ssautil/load.go +++ b/go/ssa/ssautil/load.go @@ -11,10 +11,10 @@ package ssautil import ( "go/ast" "go/token" + "go/types" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // CreateProgram returns a new program in SSA form, given a program diff --git a/go/ssa/ssautil/load_test.go b/go/ssa/ssautil/load_test.go index 31fe1866..8ce73bc2 100644 --- a/go/ssa/ssautil/load_test.go +++ b/go/ssa/ssautil/load_test.go @@ -8,15 +8,14 @@ package ssautil_test import ( "go/ast" + "go/importer" "go/parser" "go/token" + "go/types" "os" "testing" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" - - _ "golang.org/x/tools/go/gcimporter" ) const hello = `package main @@ -39,7 +38,7 @@ func TestBuildPackage(t *testing.T) { } pkg := types.NewPackage("hello", "") - ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0) + ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, 0) if err != nil { t.Fatal(err) } diff --git a/go/ssa/ssautil/switch.go b/go/ssa/ssautil/switch.go index 0cbcb3db..2fcc1672 100644 --- a/go/ssa/ssautil/switch.go +++ b/go/ssa/ssautil/switch.go @@ -24,9 +24,9 @@ import ( "bytes" "fmt" "go/token" + "go/types" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // A ConstCase represents a single constant comparison. diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go index 13d7ac14..48b184a3 100644 --- a/go/ssa/testmain.go +++ b/go/ssa/testmain.go @@ -12,13 +12,12 @@ package ssa import ( "go/ast" + exact "go/constant" "go/token" + "go/types" "os" "sort" "strings" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) // FindTests returns the list of packages that define at least one Test, diff --git a/go/ssa/util.go b/go/ssa/util.go index e3235536..317a0130 100644 --- a/go/ssa/util.go +++ b/go/ssa/util.go @@ -12,11 +12,11 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "io" "os" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" ) //// AST utilities diff --git a/go/ssa/wrappers.go b/go/ssa/wrappers.go index 75ec5968..6ca01ab3 100644 --- a/go/ssa/wrappers.go +++ b/go/ssa/wrappers.go @@ -24,7 +24,7 @@ package ssa import ( "fmt" - "golang.org/x/tools/go/types" + "go/types" ) // -- wrappers ----------------------------------------------------------- diff --git a/go/types/typeutil/example_test.go b/go/types/typeutil/example_test.go index 9e3ada7b..fe496446 100644 --- a/go/types/typeutil/example_test.go +++ b/go/types/typeutil/example_test.go @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + package typeutil_test import ( "fmt" - "sort" - "go/ast" "go/parser" "go/token" + "go/types" + "sort" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/types/typeutil/imports.go b/go/types/typeutil/imports.go index 967fe1e9..4b753f4f 100644 --- a/go/types/typeutil/imports.go +++ b/go/types/typeutil/imports.go @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + package typeutil -import "golang.org/x/tools/go/types" +import "go/types" // Dependencies returns all dependencies of the specified packages. // diff --git a/go/types/typeutil/imports14.go b/go/types/typeutil/imports14.go new file mode 100644 index 00000000..9741df36 --- /dev/null +++ b/go/types/typeutil/imports14.go @@ -0,0 +1,33 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package typeutil + +import "golang.org/x/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 +} diff --git a/go/types/typeutil/imports14_test.go b/go/types/typeutil/imports14_test.go new file mode 100644 index 00000000..b70f5f04 --- /dev/null +++ b/go/types/typeutil/imports14_test.go @@ -0,0 +1,81 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package typeutil_test + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "testing" + + "golang.org/x/tools/go/types" + "golang.org/x/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, 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) + } + } +} diff --git a/go/types/typeutil/imports_test.go b/go/types/typeutil/imports_test.go index 8071ae1b..b846fbb1 100644 --- a/go/types/typeutil/imports_test.go +++ b/go/types/typeutil/imports_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + package typeutil_test import ( @@ -9,19 +11,20 @@ import ( "go/ast" "go/parser" "go/token" + "go/types" "testing" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) +type closure map[string]*types.Package + +func (c closure) Import(path string) (*types.Package, error) { return c[path], nil } + 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 - }, + Importer: closure(packages), } fset := token.NewFileSet() @@ -30,12 +33,12 @@ func TestDependencies(t *testing.T) { // 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")`, + `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 { @@ -51,22 +54,22 @@ func TestDependencies(t *testing.T) { for _, test := range []struct { roots, want string }{ - {"A", "A"}, - {"B", "AB"}, - {"C", "AC"}, - {"D", "ABCD"}, - {"E", "ACE"}, - {"F", "ABCDEF"}, + {"a", "a"}, + {"b", "ab"}, + {"c", "ac"}, + {"d", "abcd"}, + {"e", "ace"}, + {"f", "abcdef"}, - {"BE", "ABCE"}, - {"EB", "ACEB"}, - {"DE", "ABCDE"}, - {"ED", "ACEBD"}, - {"EF", "ACEBDF"}, + {"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)]) + pkgs = append(pkgs, packages[string(r)]) } var got string for _, p := range typeutil.Dependencies(pkgs...) { diff --git a/go/types/typeutil/map.go b/go/types/typeutil/map.go index b3a04ccd..81dd556c 100644 --- a/go/types/typeutil/map.go +++ b/go/types/typeutil/map.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + // Package typeutil defines various utilities for types, such as Map, // a mapping from types.Type to interface{} values. package typeutil // import "golang.org/x/tools/go/types/typeutil" @@ -9,9 +11,8 @@ package typeutil // import "golang.org/x/tools/go/types/typeutil" import ( "bytes" "fmt" + "go/types" "reflect" - - "golang.org/x/tools/go/types" ) // Map is a hash-table-based mapping from types (types.Type) to diff --git a/go/types/typeutil/map14.go b/go/types/typeutil/map14.go new file mode 100644 index 00000000..16209e3b --- /dev/null +++ b/go/types/typeutil/map14.go @@ -0,0 +1,316 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +// Package typeutil defines various utilities for types, such as Map, +// a mapping from types.Type to interface{} values. +package typeutil // import "golang.org/x/tools/go/types/typeutil" + +import ( + "bytes" + "fmt" + "reflect" + + "golang.org/x/tools/go/types" +) + +// Map is a hash-table-based mapping from types (types.Type) to +// arbitrary interface{} values. The concrete types that implement +// the Type interface are pointers. Since they are not canonicalized, +// == cannot be used to check for equivalence, and thus we cannot +// simply use a Go map. +// +// Just as with map[K]V, a nil *Map is a valid empty map. +// +// Not thread-safe. +// +type Map struct { + hasher Hasher // shared by many Maps + table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused + length int // number of map entries +} + +// entry is an entry (key/value association) in a hash bucket. +type entry struct { + key types.Type + value interface{} +} + +// SetHasher sets the hasher used by Map. +// +// All Hashers are functionally equivalent but contain internal state +// used to cache the results of hashing previously seen types. +// +// A single Hasher created by MakeHasher() may be shared among many +// Maps. This is recommended if the instances have many keys in +// common, as it will amortize the cost of hash computation. +// +// A Hasher may grow without bound as new types are seen. Even when a +// type is deleted from the map, the Hasher never shrinks, since other +// types in the map may reference the deleted type indirectly. +// +// Hashers are not thread-safe, and read-only operations such as +// Map.Lookup require updates to the hasher, so a full Mutex lock (not a +// read-lock) is require around all Map operations if a shared +// hasher is accessed from multiple threads. +// +// If SetHasher is not called, the Map will create a private hasher at +// the first call to Insert. +// +func (m *Map) SetHasher(hasher Hasher) { + m.hasher = hasher +} + +// Delete removes the entry with the given key, if any. +// It returns true if the entry was found. +// +func (m *Map) Delete(key types.Type) bool { + if m != nil && m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + for i, e := range bucket { + if e.key != nil && types.Identical(key, e.key) { + // We can't compact the bucket as it + // would disturb iterators. + bucket[i] = entry{} + m.length-- + return true + } + } + } + return false +} + +// At returns the map entry for the given key. +// The result is nil if the entry is not present. +// +func (m *Map) At(key types.Type) interface{} { + if m != nil && m.table != nil { + for _, e := range m.table[m.hasher.Hash(key)] { + if e.key != nil && types.Identical(key, e.key) { + return e.value + } + } + } + return nil +} + +// Set sets the map entry for key to val, +// and returns the previous entry, if any. +func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { + if m.table != nil { + hash := m.hasher.Hash(key) + bucket := m.table[hash] + var hole *entry + for i, e := range bucket { + if e.key == nil { + hole = &bucket[i] + } else if types.Identical(key, e.key) { + prev = e.value + bucket[i].value = value + return + } + } + + if hole != nil { + *hole = entry{key, value} // overwrite deleted entry + } else { + m.table[hash] = append(bucket, entry{key, value}) + } + } else { + if m.hasher.memo == nil { + m.hasher = MakeHasher() + } + hash := m.hasher.Hash(key) + m.table = map[uint32][]entry{hash: {entry{key, value}}} + } + + m.length++ + return +} + +// Len returns the number of map entries. +func (m *Map) Len() int { + if m != nil { + return m.length + } + return 0 +} + +// Iterate calls function f on each entry in the map in unspecified order. +// +// If f should mutate the map, Iterate provides the same guarantees as +// Go maps: if f deletes a map entry that Iterate has not yet reached, +// f will not be invoked for it, but if f inserts a map entry that +// Iterate has not yet reached, whether or not f will be invoked for +// it is unspecified. +// +func (m *Map) Iterate(f func(key types.Type, value interface{})) { + if m != nil { + for _, bucket := range m.table { + for _, e := range bucket { + if e.key != nil { + f(e.key, e.value) + } + } + } + } +} + +// Keys returns a new slice containing the set of map keys. +// The order is unspecified. +func (m *Map) Keys() []types.Type { + keys := make([]types.Type, 0, m.Len()) + m.Iterate(func(key types.Type, _ interface{}) { + keys = append(keys, key) + }) + return keys +} + +func (m *Map) toString(values bool) string { + if m == nil { + return "{}" + } + var buf bytes.Buffer + fmt.Fprint(&buf, "{") + sep := "" + m.Iterate(func(key types.Type, value interface{}) { + fmt.Fprint(&buf, sep) + sep = ", " + fmt.Fprint(&buf, key) + if values { + fmt.Fprintf(&buf, ": %q", value) + } + }) + fmt.Fprint(&buf, "}") + return buf.String() +} + +// String returns a string representation of the map's entries. +// Values are printed using fmt.Sprintf("%v", v). +// Order is unspecified. +// +func (m *Map) String() string { + return m.toString(true) +} + +// KeysString returns a string representation of the map's key set. +// Order is unspecified. +// +func (m *Map) KeysString() string { + return m.toString(false) +} + +//////////////////////////////////////////////////////////////////////// +// Hasher + +// A Hasher maps each type to its hash value. +// For efficiency, a hasher uses memoization; thus its memory +// footprint grows monotonically over time. +// Hashers are not thread-safe. +// Hashers have reference semantics. +// Call MakeHasher to create a Hasher. +type Hasher struct { + memo map[types.Type]uint32 +} + +// MakeHasher returns a new Hasher instance. +func MakeHasher() Hasher { + return Hasher{make(map[types.Type]uint32)} +} + +// Hash computes a hash value for the given type t such that +// Identical(t, t') => Hash(t) == Hash(t'). +func (h Hasher) Hash(t types.Type) uint32 { + hash, ok := h.memo[t] + if !ok { + hash = h.hashFor(t) + h.memo[t] = hash + } + return hash +} + +// hashString computes the Fowler–Noll–Vo hash of s. +func hashString(s string) uint32 { + var h uint32 + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + +// hashFor computes the hash of t. +func (h Hasher) hashFor(t types.Type) uint32 { + // See Identical for rationale. + switch t := t.(type) { + case *types.Basic: + return uint32(t.Kind()) + + case *types.Array: + return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) + + case *types.Slice: + return 9049 + 2*h.Hash(t.Elem()) + + case *types.Struct: + var hash uint32 = 9059 + for i, n := 0, t.NumFields(); i < n; i++ { + f := t.Field(i) + if f.Anonymous() { + hash += 8861 + } + hash += hashString(t.Tag(i)) + hash += hashString(f.Name()) // (ignore f.Pkg) + hash += h.Hash(f.Type()) + } + return hash + + case *types.Pointer: + return 9067 + 2*h.Hash(t.Elem()) + + case *types.Signature: + var hash uint32 = 9091 + if t.Variadic() { + hash *= 8863 + } + return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) + + case *types.Interface: + var hash uint32 = 9103 + for i, n := 0, t.NumMethods(); i < n; i++ { + // See go/types.identicalMethods for rationale. + // Method order is not significant. + // Ignore m.Pkg(). + m := t.Method(i) + hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type()) + } + return hash + + case *types.Map: + return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) + + case *types.Chan: + return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) + + case *types.Named: + // Not safe with a copying GC; objects may move. + return uint32(reflect.ValueOf(t.Obj()).Pointer()) + + case *types.Tuple: + return h.hashTuple(t) + } + panic(t) +} + +func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { + // See go/types.identicalTypes for rationale. + n := tuple.Len() + var hash uint32 = 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 3 * h.Hash(tuple.At(i).Type()) + } + return hash +} diff --git a/go/types/typeutil/map14_test.go b/go/types/typeutil/map14_test.go new file mode 100644 index 00000000..9043d05f --- /dev/null +++ b/go/types/typeutil/map14_test.go @@ -0,0 +1,176 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package typeutil_test + +// TODO(adonovan): +// - test use of explicit hasher across two maps. +// - test hashcodes are consistent with equals for a range of types +// (e.g. all types generated by type-checking some body of real code). + +import ( + "testing" + + "golang.org/x/tools/go/types" + "golang.org/x/tools/go/types/typeutil" +) + +var ( + tStr = types.Typ[types.String] // string + tPStr1 = types.NewPointer(tStr) // *string + tPStr2 = types.NewPointer(tStr) // *string, again + tInt = types.Typ[types.Int] // int + tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int + tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again +) + +func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { + if !types.Identical(x, y) { + t.Errorf("%s: not equal: %s, %s", comment, x, y) + } + if x == y { + t.Errorf("%s: identical: %v, %v", comment, x, y) + } +} + +func TestAxioms(t *testing.T) { + checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}") + checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}") +} + +func TestMap(t *testing.T) { + var tmap *typeutil.Map + + // All methods but Set are safe on on (*T)(nil). + tmap.Len() + tmap.At(tPStr1) + tmap.Delete(tPStr1) + tmap.KeysString() + tmap.String() + + tmap = new(typeutil.Map) + + // Length of empty map. + if l := tmap.Len(); l != 0 { + t.Errorf("Len() on empty Map: got %d, want 0", l) + } + // At of missing key. + if v := tmap.At(tPStr1); v != nil { + t.Errorf("At() on empty Map: got %v, want nil", v) + } + // Deletion of missing key. + if tmap.Delete(tPStr1) { + t.Errorf("Delete() on empty Map: got true, want false") + } + // Set of new key. + if prev := tmap.Set(tPStr1, "*string"); prev != nil { + t.Errorf("Set() on empty Map returned non-nil previous value %s", prev) + } + + // Now: {*string: "*string"} + + // Length of non-empty map. + if l := tmap.Len(); l != 1 { + t.Errorf("Len(): got %d, want 1", l) + } + // At via insertion key. + if v := tmap.At(tPStr1); v != "*string" { + t.Errorf("At(): got %q, want \"*string\"", v) + } + // At via equal key. + if v := tmap.At(tPStr2); v != "*string" { + t.Errorf("At(): got %q, want \"*string\"", v) + } + // Iteration over sole entry. + tmap.Iterate(func(key types.Type, value interface{}) { + if key != tPStr1 { + t.Errorf("Iterate: key: got %s, want %s", key, tPStr1) + } + if want := "*string"; value != want { + t.Errorf("Iterate: value: got %s, want %s", value, want) + } + }) + + // Setion with key equal to present one. + if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" { + t.Errorf("Set() previous value: got %s, want \"*string\"", prev) + } + + // Setion of another association. + if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil { + t.Errorf("Set() previous value: got %s, want nil", prev) + } + + // Now: {*string: "*string again", <-chan int: "<-chan int"} + + want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}" + want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}" + if s := tmap.String(); s != want1 && s != want2 { + t.Errorf("String(): got %s, want %s", s, want1) + } + + want1 = "{*string, <-chan int}" + want2 = "{<-chan int, *string}" + if s := tmap.KeysString(); s != want1 && s != want2 { + t.Errorf("KeysString(): got %s, want %s", s, want1) + } + + // Keys(). + I := types.Identical + switch k := tmap.Keys(); { + case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok + case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok + default: + t.Errorf("Keys(): got %v, want %s", k, want2) + } + + if l := tmap.Len(); l != 2 { + t.Errorf("Len(): got %d, want 1", l) + } + // At via original key. + if v := tmap.At(tPStr1); v != "*string again" { + t.Errorf("At(): got %q, want \"*string again\"", v) + } + hamming := 1 + tmap.Iterate(func(key types.Type, value interface{}) { + switch { + case I(key, tChanInt1): + hamming *= 2 // ok + case I(key, tPStr1): + hamming *= 3 // ok + } + }) + if hamming != 6 { + t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6) + } + + if v := tmap.At(tChanInt2); v != "<-chan int" { + t.Errorf("At(): got %q, want \"<-chan int\"", v) + } + // Deletion with key equal to present one. + if !tmap.Delete(tChanInt2) { + t.Errorf("Delete() of existing key: got false, want true") + } + + // Now: {*string: "*string again"} + + if l := tmap.Len(); l != 1 { + t.Errorf("Len(): got %d, want 1", l) + } + // Deletion again. + if !tmap.Delete(tPStr2) { + t.Errorf("Delete() of existing key: got false, want true") + } + + // Now: {} + + if l := tmap.Len(); l != 0 { + t.Errorf("Len(): got %d, want %d", l, 0) + } + if s := tmap.String(); s != "{}" { + t.Errorf("Len(): got %q, want %q", s, "") + } +} diff --git a/go/types/typeutil/map_test.go b/go/types/typeutil/map_test.go index 776b5e2c..e5dc4e40 100644 --- a/go/types/typeutil/map_test.go +++ b/go/types/typeutil/map_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + package typeutil_test // TODO(adonovan): @@ -10,9 +12,9 @@ package typeutil_test // (e.g. all types generated by type-checking some body of real code). import ( + "go/types" "testing" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/go/types/typeutil/methodsetcache.go b/go/types/typeutil/methodsetcache.go index daad6445..edc3f5b8 100644 --- a/go/types/typeutil/methodsetcache.go +++ b/go/types/typeutil/methodsetcache.go @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + // This file implements a cache of method sets. package typeutil import ( + "go/types" "sync" - - "golang.org/x/tools/go/types" ) // A MethodSetCache records the method set of each type T for which diff --git a/go/types/typeutil/methodsetcache14.go b/go/types/typeutil/methodsetcache14.go new file mode 100644 index 00000000..83b5e764 --- /dev/null +++ b/go/types/typeutil/methodsetcache14.go @@ -0,0 +1,75 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +// This file implements a cache of method sets. + +package typeutil + +import ( + "sync" + + "golang.org/x/tools/go/types" +) + +// A MethodSetCache records the method set of each type T for which +// MethodSet(T) is called so that repeat queries are fast. +// The zero value is a ready-to-use cache instance. +type MethodSetCache struct { + mu sync.Mutex + named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N + others map[types.Type]*types.MethodSet // all other types +} + +// MethodSet returns the method set of type T. It is thread-safe. +// +// If cache is nil, this function is equivalent to types.NewMethodSet(T). +// Utility functions can thus expose an optional *MethodSetCache +// parameter to clients that care about performance. +// +func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { + if cache == nil { + return types.NewMethodSet(T) + } + cache.mu.Lock() + defer cache.mu.Unlock() + + switch T := T.(type) { + case *types.Named: + return cache.lookupNamed(T).value + + case *types.Pointer: + if N, ok := T.Elem().(*types.Named); ok { + return cache.lookupNamed(N).pointer + } + } + + // all other types + // (The map uses pointer equivalence, not type identity.) + mset := cache.others[T] + if mset == nil { + mset = types.NewMethodSet(T) + if cache.others == nil { + cache.others = make(map[types.Type]*types.MethodSet) + } + cache.others[T] = mset + } + return mset +} + +func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { + if cache.named == nil { + cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) + } + // Avoid recomputing mset(*T) for each distinct Pointer + // instance whose underlying type is a named type. + msets, ok := cache.named[named] + if !ok { + msets.value = types.NewMethodSet(named) + msets.pointer = types.NewMethodSet(types.NewPointer(named)) + cache.named[named] = msets + } + return msets +} diff --git a/go/types/typeutil/ui.go b/go/types/typeutil/ui.go index 20c5249a..945fb297 100644 --- a/go/types/typeutil/ui.go +++ b/go/types/typeutil/ui.go @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build go1.5 + package typeutil // This file defines utilities for user interfaces that display types. -import "golang.org/x/tools/go/types" +import "go/types" // IntuitiveMethodSet returns the intuitive method set of a type, T. // diff --git a/go/types/typeutil/ui14.go b/go/types/typeutil/ui14.go new file mode 100644 index 00000000..bb78e0b2 --- /dev/null +++ b/go/types/typeutil/ui14.go @@ -0,0 +1,40 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package typeutil + +// This file defines utilities for user interfaces that display types. + +import "golang.org/x/tools/go/types" + +// IntuitiveMethodSet returns the intuitive method set of a type, T. +// +// The result contains MethodSet(T) and additionally, if T is a +// concrete type, methods belonging to *T if there is no identically +// named method on T itself. This corresponds to user intuition about +// method sets; this function is intended only for user interfaces. +// +// The order of the result is as for types.MethodSet(T). +// +func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { + var result []*types.Selection + mset := msets.MethodSet(T) + if _, ok := T.Underlying().(*types.Interface); ok { + for i, n := 0, mset.Len(); i < n; i++ { + result = append(result, mset.At(i)) + } + } else { + pmset := msets.MethodSet(types.NewPointer(T)) + for i, n := 0, pmset.Len(); i < n; i++ { + meth := pmset.At(i) + if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { + meth = m + } + result = append(result, meth) + } + } + return result +} diff --git a/godoc/analysis/analysis.go b/godoc/analysis/analysis.go index 865f9601..5c43bbe0 100644 --- a/godoc/analysis/analysis.go +++ b/godoc/analysis/analysis.go @@ -47,8 +47,10 @@ package analysis // import "golang.org/x/tools/godoc/analysis" import ( "fmt" "go/build" + exact "go/constant" "go/scanner" "go/token" + "go/types" "html" "io" "log" @@ -59,12 +61,10 @@ import ( "strings" "sync" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" ) // -- links ------------------------------------------------------------ diff --git a/godoc/analysis/callgraph.go b/godoc/analysis/callgraph.go index 75b9691d..4e97061f 100644 --- a/godoc/analysis/callgraph.go +++ b/godoc/analysis/callgraph.go @@ -14,13 +14,13 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "log" "math/big" "sort" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) // doCallgraph computes the CALLEES and CALLERS relations. diff --git a/godoc/analysis/implements.go b/godoc/analysis/implements.go index b681da52..1c856c37 100644 --- a/godoc/analysis/implements.go +++ b/godoc/analysis/implements.go @@ -13,9 +13,9 @@ package analysis // belong to different packages and at least one is not exported? import ( + "go/types" "sort" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/godoc/analysis/peers.go b/godoc/analysis/peers.go index 19bd8bab..74a08a1f 100644 --- a/godoc/analysis/peers.go +++ b/godoc/analysis/peers.go @@ -16,10 +16,10 @@ package analysis import ( "fmt" "go/token" + "go/types" "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" ) func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) { diff --git a/godoc/analysis/typeinfo.go b/godoc/analysis/typeinfo.go index f61a4d36..1ccff52f 100644 --- a/godoc/analysis/typeinfo.go +++ b/godoc/analysis/typeinfo.go @@ -22,12 +22,12 @@ package analysis import ( "fmt" + "go/types" "reflect" "strconv" "strings" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" ) diff --git a/oracle/callees.go b/oracle/callees.go index d25b2a11..3029eed2 100644 --- a/oracle/callees.go +++ b/oracle/callees.go @@ -10,13 +10,13 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "sort" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/definition.go b/oracle/definition.go index 8a691cad..67f65f97 100644 --- a/oracle/definition.go +++ b/oracle/definition.go @@ -10,9 +10,9 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/describe.go b/oracle/describe.go index 7782eaf4..2743f67c 100644 --- a/oracle/describe.go +++ b/oracle/describe.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build go1.5 +// +build go1.6 package oracle @@ -10,15 +10,15 @@ import ( "bytes" "fmt" "go/ast" + exact "go/constant" "go/token" + "go/types" "log" "os" "strings" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/describe14.go b/oracle/describe14.go index 510d1b2f..e7784185 100644 --- a/oracle/describe14.go +++ b/oracle/describe14.go @@ -350,7 +350,7 @@ type describeValueResult struct { func (r *describeValueResult) display(printf printfFunc) { var prefix, suffix string if r.constVal != nil { - suffix = fmt.Sprintf(" of constant value %s", r.constVal) + suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal)) } switch obj := r.obj.(type) { case *types.Func: @@ -607,7 +607,7 @@ func formatMember(obj types.Object, maxname int) string { fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) switch obj := obj.(type) { case *types.Const: - fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val().String()) + fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val())) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) @@ -644,7 +644,7 @@ func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet var val string switch mem := mem.obj.(type) { case *types.Const: - val = mem.Val().String() + val = constValString(mem.Val()) case *types.TypeName: typ = typ.Underlying() } @@ -767,3 +767,14 @@ func methodsToSerial(this *types.Package, methods []*types.Selection, fset *toke } return jmethods } + +// constValString emulates Go 1.6's go/constant.ExactString well enough +// to make the tests pass. This is just a stopgap until we throw away +// all the *14.go files. +func constValString(v exact.Value) string { + if v.Kind() == exact.Float { + f, _ := exact.Float64Val(v) + return fmt.Sprintf("%g", f) + } + return v.String() +} diff --git a/oracle/describe15.go b/oracle/describe15.go new file mode 100644 index 00000000..6f812e6c --- /dev/null +++ b/oracle/describe15.go @@ -0,0 +1,780 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5,!go1.6 + +package oracle + +import ( + "bytes" + "fmt" + "go/ast" + exact "go/constant" + "go/token" + "go/types" + "log" + "os" + "strings" + + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/loader" + "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/oracle/serial" +) + +// describe describes the syntax node denoted by the query position, +// including: +// - its syntactic category +// - the definition of its referent (for identifiers) [now redundant] +// - its type and method set (for an expression or type expression) +// +func describe(q *Query) error { + lconf := loader.Config{Build: q.Build} + allowErrors(&lconf) + + if _, err := importQueryPackage(q.Pos, &lconf); err != nil { + return err + } + + // Load/parse/type-check the program. + lprog, err := lconf.Load() + if err != nil { + return err + } + q.Fset = lprog.Fset + + qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos) + if err != nil { + return err + } + + if false { // debugging + fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s", + astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path)) + } + + path, action := findInterestingNode(qpos.info, qpos.path) + switch action { + case actionExpr: + q.result, err = describeValue(qpos, path) + + case actionType: + q.result, err = describeType(qpos, path) + + case actionPackage: + q.result, err = describePackage(qpos, path) + + case actionStmt: + q.result, err = describeStmt(qpos, path) + + case actionUnknown: + q.result = &describeUnknownResult{path[0]} + + default: + panic(action) // unreachable + } + return err +} + +type describeUnknownResult struct { + node ast.Node +} + +func (r *describeUnknownResult) display(printf printfFunc) { + // Nothing much to say about misc syntax. + printf(r.node, "%s", astutil.NodeDescription(r.node)) +} + +func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { + res.Describe = &serial.Describe{ + Desc: astutil.NodeDescription(r.node), + Pos: fset.Position(r.node.Pos()).String(), + } +} + +type action int + +const ( + actionUnknown action = iota // None of the below + actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var}) + actionType // type Expr or Ident(types.TypeName). + actionStmt // Stmt or Ident(types.Label) + actionPackage // Ident(types.Package) or ImportSpec +) + +// findInterestingNode classifies the syntax node denoted by path as one of: +// - an expression, part of an expression or a reference to a constant +// or variable; +// - a type, part of a type, or a reference to a named type; +// - a statement, part of a statement, or a label referring to a statement; +// - part of a package declaration or import spec. +// - none of the above. +// and returns the most "interesting" associated node, which may be +// the same node, an ancestor or a descendent. +// +func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) { + // TODO(adonovan): integrate with go/types/stdlib_test.go and + // apply this to every AST node we can find to make sure it + // doesn't crash. + + // TODO(adonovan): audit for ParenExpr safety, esp. since we + // traverse up and down. + + // TODO(adonovan): if the users selects the "." in + // "fmt.Fprintf()", they'll get an ambiguous selection error; + // we won't even reach here. Can we do better? + + // TODO(adonovan): describing a field within 'type T struct {...}' + // describes the (anonymous) struct type and concludes "no methods". + // We should ascend to the enclosing type decl, if any. + + for len(path) > 0 { + switch n := path[0].(type) { + case *ast.GenDecl: + if len(n.Specs) == 1 { + // Descend to sole {Import,Type,Value}Spec child. + path = append([]ast.Node{n.Specs[0]}, path...) + continue + } + return path, actionUnknown // uninteresting + + case *ast.FuncDecl: + // Descend to function name. + path = append([]ast.Node{n.Name}, path...) + continue + + case *ast.ImportSpec: + return path, actionPackage + + case *ast.ValueSpec: + if len(n.Names) == 1 { + // Descend to sole Ident child. + path = append([]ast.Node{n.Names[0]}, path...) + continue + } + return path, actionUnknown // uninteresting + + case *ast.TypeSpec: + // Descend to type name. + path = append([]ast.Node{n.Name}, path...) + continue + + case ast.Stmt: + return path, actionStmt + + case *ast.ArrayType, + *ast.StructType, + *ast.FuncType, + *ast.InterfaceType, + *ast.MapType, + *ast.ChanType: + return path, actionType + + case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause: + return path, actionUnknown // uninteresting + + case *ast.Ellipsis: + // Continue to enclosing node. + // e.g. [...]T in ArrayType + // f(x...) in CallExpr + // f(x...T) in FuncType + + case *ast.Field: + // TODO(adonovan): this needs more thought, + // since fields can be so many things. + if len(n.Names) == 1 { + // Descend to sole Ident child. + path = append([]ast.Node{n.Names[0]}, path...) + continue + } + // Zero names (e.g. anon field in struct) + // or multiple field or param names: + // continue to enclosing field list. + + case *ast.FieldList: + // Continue to enclosing node: + // {Struct,Func,Interface}Type or FuncDecl. + + case *ast.BasicLit: + if _, ok := path[1].(*ast.ImportSpec); ok { + return path[1:], actionPackage + } + return path, actionExpr + + case *ast.SelectorExpr: + // TODO(adonovan): use Selections info directly. + if pkginfo.Uses[n.Sel] == nil { + // TODO(adonovan): is this reachable? + return path, actionUnknown + } + // Descend to .Sel child. + path = append([]ast.Node{n.Sel}, path...) + continue + + case *ast.Ident: + switch pkginfo.ObjectOf(n).(type) { + case *types.PkgName: + return path, actionPackage + + case *types.Const: + return path, actionExpr + + case *types.Label: + return path, actionStmt + + case *types.TypeName: + return path, actionType + + case *types.Var: + // For x in 'struct {x T}', return struct type, for now. + if _, ok := path[1].(*ast.Field); ok { + _ = path[2].(*ast.FieldList) // assertion + if _, ok := path[3].(*ast.StructType); ok { + return path[3:], actionType + } + } + return path, actionExpr + + case *types.Func: + return path, actionExpr + + case *types.Builtin: + // For reference to built-in function, return enclosing call. + path = path[1:] // ascend to enclosing function call + continue + + case *types.Nil: + return path, actionExpr + } + + // No object. + switch path[1].(type) { + case *ast.SelectorExpr: + // Return enclosing selector expression. + return path[1:], actionExpr + + case *ast.Field: + // TODO(adonovan): test this. + // e.g. all f in: + // struct { f, g int } + // interface { f() } + // func (f T) method(f, g int) (f, g bool) + // + // switch path[3].(type) { + // case *ast.FuncDecl: + // case *ast.StructType: + // case *ast.InterfaceType: + // } + // + // return path[1:], actionExpr + // + // Unclear what to do with these. + // Struct.Fields -- field + // Interface.Methods -- field + // FuncType.{Params.Results} -- actionExpr + // FuncDecl.Recv -- actionExpr + + case *ast.File: + // 'package foo' + return path, actionPackage + + case *ast.ImportSpec: + // TODO(adonovan): fix: why no package object? go/types bug? + return path[1:], actionPackage + + default: + // e.g. blank identifier + // or y in "switch y := x.(type)" + // or code in a _test.go file that's not part of the package. + log.Printf("unknown reference %s in %T\n", n, path[1]) + return path, actionUnknown + } + + case *ast.StarExpr: + if pkginfo.Types[n].IsType() { + return path, actionType + } + return path, actionExpr + + case ast.Expr: + // All Expr but {BasicLit,Ident,StarExpr} are + // "true" expressions that evaluate to a value. + return path, actionExpr + } + + // Ascend to parent. + path = path[1:] + } + + return nil, actionUnknown // unreachable +} + +func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) { + var expr ast.Expr + var obj types.Object + switch n := path[0].(type) { + case *ast.ValueSpec: + // ambiguous ValueSpec containing multiple names + return nil, fmt.Errorf("multiple value specification") + case *ast.Ident: + obj = qpos.info.ObjectOf(n) + expr = n + case ast.Expr: + expr = n + default: + // TODO(adonovan): is this reachable? + return nil, fmt.Errorf("unexpected AST for expr: %T", n) + } + + typ := qpos.info.TypeOf(expr) + constVal := qpos.info.Types[expr].Value + + return &describeValueResult{ + qpos: qpos, + expr: expr, + typ: typ, + constVal: constVal, + obj: obj, + }, nil +} + +type describeValueResult struct { + qpos *queryPos + expr ast.Expr // query node + typ types.Type // type of expression + constVal exact.Value // value of expression, if constant + obj types.Object // var/func/const object, if expr was Ident +} + +func (r *describeValueResult) display(printf printfFunc) { + var prefix, suffix string + if r.constVal != nil { + suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal)) + } + switch obj := r.obj.(type) { + case *types.Func: + if recv := obj.Type().(*types.Signature).Recv(); recv != nil { + if _, ok := recv.Type().Underlying().(*types.Interface); ok { + prefix = "interface method " + } else { + prefix = "method " + } + } + } + + // Describe the expression. + if r.obj != nil { + if r.obj.Pos() == r.expr.Pos() { + // defining ident + printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) + } else { + // referring ident + printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix) + if def := r.obj.Pos(); def != token.NoPos { + printf(def, "defined here") + } + } + } else { + desc := astutil.NodeDescription(r.expr) + if suffix != "" { + // constant expression + printf(r.expr, "%s%s", desc, suffix) + } else { + // non-constant expression + printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ)) + } + } +} + +func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { + var value, objpos string + if r.constVal != nil { + value = r.constVal.String() + } + if r.obj != nil { + objpos = fset.Position(r.obj.Pos()).String() + } + + res.Describe = &serial.Describe{ + Desc: astutil.NodeDescription(r.expr), + Pos: fset.Position(r.expr.Pos()).String(), + Detail: "value", + Value: &serial.DescribeValue{ + Type: r.qpos.typeString(r.typ), + Value: value, + ObjPos: objpos, + }, + } +} + +// ---- TYPE ------------------------------------------------------------ + +func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { + var description string + var t types.Type + switch n := path[0].(type) { + case *ast.Ident: + t = qpos.info.TypeOf(n) + switch t := t.(type) { + case *types.Basic: + description = "reference to built-in " + + case *types.Named: + isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above + if isDef { + description = "definition of " + } else { + description = "reference to " + } + } + + case ast.Expr: + t = qpos.info.TypeOf(n) + + default: + // Unreachable? + return nil, fmt.Errorf("unexpected AST for type: %T", n) + } + + description = description + "type " + qpos.typeString(t) + + // Show sizes for structs and named types (it's fairly obvious for others). + switch t.(type) { + case *types.Named, *types.Struct: + szs := types.StdSizes{8, 8} // assume amd64 + description = fmt.Sprintf("%s (size %d, align %d)", description, + szs.Sizeof(t), szs.Alignof(t)) + } + + return &describeTypeResult{ + qpos: qpos, + node: path[0], + description: description, + typ: t, + methods: accessibleMethods(t, qpos.info.Pkg), + }, nil +} + +type describeTypeResult struct { + qpos *queryPos + node ast.Node + description string + typ types.Type + methods []*types.Selection +} + +func (r *describeTypeResult) display(printf printfFunc) { + printf(r.node, "%s", r.description) + + // Show the underlying type for a reference to a named type. + if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() { + printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying())) + } + + // Print the method set, if the type kind is capable of bearing methods. + switch r.typ.(type) { + case *types.Interface, *types.Struct, *types.Named: + if len(r.methods) > 0 { + printf(r.node, "Method set:") + for _, meth := range r.methods { + // TODO(adonovan): print these relative + // to the owning package, not the + // query package. + printf(meth.Obj(), "\t%s", r.qpos.selectionString(meth)) + } + } else { + printf(r.node, "No methods.") + } + } +} + +func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) { + var namePos, nameDef string + if nt, ok := r.typ.(*types.Named); ok { + namePos = fset.Position(nt.Obj().Pos()).String() + nameDef = nt.Underlying().String() + } + res.Describe = &serial.Describe{ + Desc: r.description, + Pos: fset.Position(r.node.Pos()).String(), + Detail: "type", + Type: &serial.DescribeType{ + Type: r.qpos.typeString(r.typ), + NamePos: namePos, + NameDef: nameDef, + Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset), + }, + } +} + +// ---- PACKAGE ------------------------------------------------------------ + +func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) { + var description string + var pkg *types.Package + switch n := path[0].(type) { + case *ast.ImportSpec: + var obj types.Object + if n.Name != nil { + obj = qpos.info.Defs[n.Name] + } else { + obj = qpos.info.Implicits[n] + } + pkgname, _ := obj.(*types.PkgName) + if pkgname == nil { + return nil, fmt.Errorf("can't import package %s", n.Path.Value) + } + pkg = pkgname.Imported() + description = fmt.Sprintf("import of package %q", pkg.Path()) + + case *ast.Ident: + if _, isDef := path[1].(*ast.File); isDef { + // e.g. package id + pkg = qpos.info.Pkg + description = fmt.Sprintf("definition of package %q", pkg.Path()) + } else { + // e.g. import id "..." + // or id.F() + pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported() + description = fmt.Sprintf("reference to package %q", pkg.Path()) + } + + default: + // Unreachable? + return nil, fmt.Errorf("unexpected AST for package: %T", n) + } + + var members []*describeMember + // NB: "unsafe" has no types.Package + if pkg != nil { + // Enumerate the accessible package members + // in lexicographic order. + for _, name := range pkg.Scope().Names() { + if pkg == qpos.info.Pkg || ast.IsExported(name) { + mem := pkg.Scope().Lookup(name) + var methods []*types.Selection + if mem, ok := mem.(*types.TypeName); ok { + methods = accessibleMethods(mem.Type(), qpos.info.Pkg) + } + members = append(members, &describeMember{ + mem, + methods, + }) + + } + } + } + + return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil +} + +type describePackageResult struct { + fset *token.FileSet + node ast.Node + description string + pkg *types.Package + members []*describeMember // in lexicographic name order +} + +type describeMember struct { + obj types.Object + methods []*types.Selection // in types.MethodSet order +} + +func (r *describePackageResult) display(printf printfFunc) { + printf(r.node, "%s", r.description) + + // Compute max width of name "column". + maxname := 0 + for _, mem := range r.members { + if l := len(mem.obj.Name()); l > maxname { + maxname = l + } + } + + for _, mem := range r.members { + printf(mem.obj, "\t%s", formatMember(mem.obj, maxname)) + for _, meth := range mem.methods { + printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg))) + } + } +} + +func formatMember(obj types.Object, maxname int) string { + qualifier := types.RelativeTo(obj.Pkg()) + var buf bytes.Buffer + fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) + switch obj := obj.(type) { + case *types.Const: + fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val())) + + case *types.Func: + fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) + + case *types.TypeName: + // Abbreviate long aggregate type names. + var abbrev string + switch t := obj.Type().Underlying().(type) { + case *types.Interface: + if t.NumMethods() > 1 { + abbrev = "interface{...}" + } + case *types.Struct: + if t.NumFields() > 1 { + abbrev = "struct{...}" + } + } + if abbrev == "" { + fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) + } else { + fmt.Fprintf(&buf, " %s", abbrev) + } + + case *types.Var: + fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) + } + return buf.String() +} + +func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) { + var members []*serial.DescribeMember + for _, mem := range r.members { + typ := mem.obj.Type() + var val string + switch mem := mem.obj.(type) { + case *types.Const: + val = constValString(mem.Val()) + case *types.TypeName: + typ = typ.Underlying() + } + members = append(members, &serial.DescribeMember{ + Name: mem.obj.Name(), + Type: typ.String(), + Value: val, + Pos: fset.Position(mem.obj.Pos()).String(), + Kind: tokenOf(mem.obj), + Methods: methodsToSerial(r.pkg, mem.methods, fset), + }) + } + res.Describe = &serial.Describe{ + Desc: r.description, + Pos: fset.Position(r.node.Pos()).String(), + Detail: "package", + Package: &serial.DescribePackage{ + Path: r.pkg.Path(), + Members: members, + }, + } +} + +func tokenOf(o types.Object) string { + switch o.(type) { + case *types.Func: + return "func" + case *types.Var: + return "var" + case *types.TypeName: + return "type" + case *types.Const: + return "const" + case *types.PkgName: + return "package" + } + panic(o) +} + +// ---- STATEMENT ------------------------------------------------------------ + +func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) { + var description string + switch n := path[0].(type) { + case *ast.Ident: + if qpos.info.Defs[n] != nil { + description = "labelled statement" + } else { + description = "reference to labelled statement" + } + + default: + // Nothing much to say about statements. + description = astutil.NodeDescription(n) + } + return &describeStmtResult{qpos.fset, path[0], description}, nil +} + +type describeStmtResult struct { + fset *token.FileSet + node ast.Node + description string +} + +func (r *describeStmtResult) display(printf printfFunc) { + printf(r.node, "%s", r.description) +} + +func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) { + res.Describe = &serial.Describe{ + Desc: r.description, + Pos: fset.Position(r.node.Pos()).String(), + Detail: "unknown", + } +} + +// ------------------- Utilities ------------------- + +// pathToString returns a string containing the concrete types of the +// nodes in path. +func pathToString(path []ast.Node) string { + var buf bytes.Buffer + fmt.Fprint(&buf, "[") + for i, n := range path { + if i > 0 { + fmt.Fprint(&buf, " ") + } + fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.")) + } + fmt.Fprint(&buf, "]") + return buf.String() +} + +func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { + var methods []*types.Selection + for _, meth := range typeutil.IntuitiveMethodSet(t, nil) { + if isAccessibleFrom(meth.Obj(), from) { + methods = append(methods, meth) + } + } + return methods +} + +func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { + return ast.IsExported(obj.Name()) || obj.Pkg() == pkg +} + +func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod { + qualifier := types.RelativeTo(this) + var jmethods []serial.DescribeMethod + for _, meth := range methods { + var ser serial.DescribeMethod + if meth != nil { // may contain nils when called by implements (on a method) + ser = serial.DescribeMethod{ + Name: types.SelectionString(meth, qualifier), + Pos: fset.Position(meth.Obj().Pos()).String(), + } + } + jmethods = append(jmethods, ser) + } + return jmethods +} + +// constValString emulates Go 1.6's go/constant.ExactString well enough +// to make the tests pass. This is just a stopgap until we throw away +// all the *15.go files. +func constValString(v exact.Value) string { + if v.Kind() == exact.Float { + f, _ := exact.Float64Val(v) + return fmt.Sprintf("%g", f) + } + return v.String() +} diff --git a/oracle/freevars.go b/oracle/freevars.go index 48ccb599..6b89cb39 100644 --- a/oracle/freevars.go +++ b/oracle/freevars.go @@ -11,10 +11,10 @@ import ( "go/ast" "go/printer" "go/token" + "go/types" "sort" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/implements.go b/oracle/implements.go index ee6e3500..7c9da1dc 100644 --- a/oracle/implements.go +++ b/oracle/implements.go @@ -10,12 +10,12 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "reflect" "sort" "strings" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/oracle/serial" "golang.org/x/tools/refactor/importgraph" diff --git a/oracle/oracle.go b/oracle/oracle.go index 567629c3..794cbe90 100644 --- a/oracle/oracle.go +++ b/oracle/oracle.go @@ -27,6 +27,7 @@ import ( "go/build" "go/parser" "go/token" + "go/types" "io" "path/filepath" @@ -34,7 +35,6 @@ import ( "golang.org/x/tools/go/loader" "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/peers.go b/oracle/peers.go index a06836c0..0564516d 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -10,12 +10,12 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "sort" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/pointsto.go b/oracle/pointsto.go index cc6cd171..9b862f5e 100644 --- a/oracle/pointsto.go +++ b/oracle/pointsto.go @@ -10,6 +10,7 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "sort" "golang.org/x/tools/go/ast/astutil" @@ -17,7 +18,6 @@ import ( "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/oracle/referrers.go b/oracle/referrers.go index f30d5ea1..35454065 100644 --- a/oracle/referrers.go +++ b/oracle/referrers.go @@ -11,11 +11,11 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "io/ioutil" "sort" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" "golang.org/x/tools/refactor/importgraph" ) diff --git a/oracle/testdata/src/describe/main.golden b/oracle/testdata/src/describe/main.golden index fb880a55..74b0f803 100644 --- a/oracle/testdata/src/describe/main.golden +++ b/oracle/testdata/src/describe/main.golden @@ -10,8 +10,8 @@ definition of package "describe" type cake float64 var global *string func main func() - const pi untyped float = 3141/1000 - const pie cake = 1768225803696341/562949953421312 + const pi untyped float = 3.141 + const pie cake = 3.141 -------- @describe badimport1 -------- @@ -32,7 +32,7 @@ definition of const pi untyped float definition of const pie cake -------- @describe const-ref-pi -------- -reference to const pi untyped float of constant value 3141/1000 +reference to const pi untyped float of constant value 3.141 defined here -------- @describe func-def-main -------- @@ -120,7 +120,7 @@ definition of const localpi untyped float definition of const localpie cake -------- @describe const-ref-localpi -------- -reference to const localpi untyped float of constant value 3141/1000 +reference to const localpi untyped float of constant value 3.141 defined here -------- @describe type-def-T -------- diff --git a/oracle/whicherrs.go b/oracle/whicherrs.go index 2d61e2a4..be43c390 100644 --- a/oracle/whicherrs.go +++ b/oracle/whicherrs.go @@ -10,13 +10,13 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "sort" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" - "golang.org/x/tools/go/types" "golang.org/x/tools/oracle/serial" ) diff --git a/refactor/eg/eg.go b/refactor/eg/eg.go index 5457d23a..4d56824b 100644 --- a/refactor/eg/eg.go +++ b/refactor/eg/eg.go @@ -15,9 +15,8 @@ import ( "go/format" "go/printer" "go/token" + "go/types" "os" - - "golang.org/x/tools/go/types" ) const Help = ` diff --git a/refactor/eg/eg_test.go b/refactor/eg/eg_test.go index 6ad872ad..c2599203 100644 --- a/refactor/eg/eg_test.go +++ b/refactor/eg/eg_test.go @@ -13,8 +13,10 @@ package eg_test import ( "bytes" "flag" + exact "go/constant" "go/parser" "go/token" + "go/types" "os" "os/exec" "path/filepath" @@ -22,9 +24,7 @@ import ( "strings" "testing" - "golang.org/x/tools/go/exact" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/refactor/eg" ) diff --git a/refactor/eg/match.go b/refactor/eg/match.go index edd469cd..8d989bca 100644 --- a/refactor/eg/match.go +++ b/refactor/eg/match.go @@ -10,13 +10,13 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "log" "os" "reflect" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" ) // matchExpr reports whether pattern x matches y. diff --git a/refactor/eg/rewrite.go b/refactor/eg/rewrite.go index 8fce5588..d91a99cc 100644 --- a/refactor/eg/rewrite.go +++ b/refactor/eg/rewrite.go @@ -14,6 +14,7 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "os" "reflect" "sort" @@ -21,7 +22,6 @@ import ( "strings" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" ) // Transform applies the transformation to the specified parsed file, diff --git a/refactor/rename/check.go b/refactor/rename/check.go index 1601cf7a..cf1999e8 100644 --- a/refactor/rename/check.go +++ b/refactor/rename/check.go @@ -12,9 +12,9 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/refactor/satisfy" ) diff --git a/refactor/rename/rename.go b/refactor/rename/rename.go index 73aa29a8..526ee1e5 100644 --- a/refactor/rename/rename.go +++ b/refactor/rename/rename.go @@ -18,6 +18,7 @@ import ( "go/format" "go/parser" "go/token" + "go/types" "io" "io/ioutil" "log" @@ -29,7 +30,6 @@ import ( "strings" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/refactor/importgraph" "golang.org/x/tools/refactor/satisfy" diff --git a/refactor/rename/spec.go b/refactor/rename/spec.go index 72a2c5ff..498f8669 100644 --- a/refactor/rename/spec.go +++ b/refactor/rename/spec.go @@ -17,6 +17,7 @@ import ( "go/build" "go/parser" "go/token" + "go/types" "log" "os" "path/filepath" @@ -25,7 +26,6 @@ import ( "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" ) // A spec specifies an entity to rename. diff --git a/refactor/rename/util.go b/refactor/rename/util.go index c30c1958..f0f80f03 100644 --- a/refactor/rename/util.go +++ b/refactor/rename/util.go @@ -8,6 +8,7 @@ package rename import ( "go/ast" + "go/types" "os" "path/filepath" "reflect" @@ -16,7 +17,6 @@ import ( "unicode" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" ) func objectKind(obj types.Object) string { diff --git a/refactor/satisfy/find.go b/refactor/satisfy/find.go index 9fd84557..a346c1ac 100644 --- a/refactor/satisfy/find.go +++ b/refactor/satisfy/find.go @@ -50,9 +50,9 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" "golang.org/x/tools/go/types/typeutil" )