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 <gri@golang.org>
This commit is contained in:
Alan Donovan 2015-12-29 13:06:30 -05:00
parent 2477c0d578
commit 542ffc7e75
98 changed files with 1860 additions and 185 deletions

View File

@ -44,6 +44,7 @@ import (
"go/format" "go/format"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io" "io"
"log" "log"
"os" "os"
@ -51,7 +52,6 @@ import (
"strings" "strings"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
) )
func main() { func main() {

View File

@ -11,6 +11,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/build" "go/build"
"go/types"
"os" "os"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
@ -20,7 +21,6 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
var ( var (

View File

@ -64,20 +64,18 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/build" "go/build"
exact "go/constant"
"go/format" "go/format"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
_ "golang.org/x/tools/go/gcimporter"
) )
var ( 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. // check type-checks the package. The package must be OK to proceed.
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) { func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
pkg.defs = make(map[*ast.Ident]types.Object) pkg.defs = make(map[*ast.Ident]types.Object)
config := types.Config{FakeImportC: true} config := types.Config{Importer: importer.Default(), FakeImportC: true}
info := &types.Info{ info := &types.Info{
Defs: pkg.defs, Defs: pkg.defs,
} }

View File

@ -11,8 +11,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -6,11 +6,10 @@ package main
import ( import (
"go/ast" "go/ast"
"go/types"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -18,14 +18,12 @@ import (
"go/parser" "go/parser"
"go/printer" "go/printer"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
_ "golang.org/x/tools/go/gcimporter"
"golang.org/x/tools/go/types"
) )
var ( var (

View File

@ -12,8 +12,7 @@ package main
import ( import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -10,13 +10,12 @@ import (
"bytes" "bytes"
"flag" "flag"
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "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") var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check")

View File

@ -34,8 +34,7 @@ import (
"flag" "flag"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy") var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy")

View File

@ -10,10 +10,9 @@ package main
import ( import (
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -8,9 +8,9 @@ package main
import ( import (
"go/ast" "go/ast"
"go/importer"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
// imports is the canonical map of imported packages we need for typechecking. // 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 imports = make(map[string]*types.Package)
var ( var (
defaultImporter = importer.Default()
errorType *types.Interface errorType *types.Interface
stringerType *types.Interface // possibly nil stringerType *types.Interface // possibly nil
formatterType *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 // path.name, and adds the respective package to the imports map
// as a side effect. In case of an error, importType returns nil. // as a side effect. In case of an error, importType returns nil.
func importType(path, name string) types.Type { func importType(path, name string) types.Type {
pkg, err := types.DefaultImport(imports, path) pkg, err := defaultImporter.Import(path)
if err != nil { if err != nil {
// This can happen if the package at path hasn't been compiled yet. // This can happen if the package at path hasn't been compiled yet.
warnf("import failed: %v", err) 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.spans = make(map[types.Object]Span)
pkg.types = make(map[ast.Expr]types.TypeAndValue) pkg.types = make(map[ast.Expr]types.TypeAndValue)
config := types.Config{ config := types.Config{
// We provide the same packages map for all imports to ensure Importer: defaultImporter,
// that everybody sees identical packages for the given paths.
Packages: imports,
// By providing a Config with our own error function, it will continue // 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. // past the first error. There is no need for that function to do anything.
Error: func(error) {}, Error: func(error) {},

View File

@ -9,8 +9,7 @@ package main
import ( import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -11,9 +11,8 @@ import (
"flag" "flag"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"strings" "strings"
"golang.org/x/tools/go/types"
) )
var unusedFuncsFlag = flag.String("unusedfuncs", var unusedFuncsFlag = flag.String("unusedfuncs",

View File

@ -26,10 +26,11 @@
package cha // import "golang.org/x/tools/go/callgraph/cha" package cha // import "golang.org/x/tools/go/callgraph/cha"
import ( import (
"go/types"
"golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -16,6 +16,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"sort" "sort"
"strings" "strings"
@ -25,7 +26,6 @@ import (
"golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
var inputs = []string{ var inputs = []string{

View File

@ -50,10 +50,10 @@ package rta // import "golang.org/x/tools/go/callgraph/rta"
import ( import (
"fmt" "fmt"
"go/types"
"golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -16,6 +16,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"sort" "sort"
"strings" "strings"
@ -26,7 +27,6 @@ import (
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
var inputs = []string{ var inputs = []string{

View File

@ -15,6 +15,7 @@ import (
"go/build" "go/build"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"os" "os"
"sort" "sort"
"strings" "strings"
@ -23,7 +24,6 @@ import (
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/types"
) )
const trace = false // show timing info for type-checking 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 { if f := imp.conf.TypeCheckFuncBodies; f != nil {
tc.IgnoreFuncBodies = !f(path) tc.IgnoreFuncBodies = !f(path)
} }
tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) { tc.Importer = closure{imp, info}
return imp.doImport(info, to)
}
tc.Error = info.appendError // appendError wraps the user's Error function tc.Error = info.appendError // appendError wraps the user's Error function
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info) 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() imp.progMu.Unlock()
return info 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) }

View File

@ -16,6 +16,7 @@ import (
"go/ast" "go/ast"
"go/build" "go/build"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -25,7 +26,6 @@ import (
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
) )
func TestStdlib(t *testing.T) { func TestStdlib(t *testing.T) {

View File

@ -11,6 +11,7 @@ package pointer
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"io" "io"
"os" "os"
"reflect" "reflect"
@ -20,7 +21,6 @@ import (
"golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -6,9 +6,7 @@
package pointer package pointer
import ( import "go/types"
"golang.org/x/tools/go/types"
)
type constraint interface { type constraint interface {
// For a complex constraint, returns the nodeid of the pointer // For a complex constraint, returns the nodeid of the pointer

View File

@ -15,10 +15,10 @@ package pointer
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
var ( var (

View File

@ -165,11 +165,11 @@ package pointer
import ( import (
"fmt" "fmt"
"go/types"
"io" "io"
"reflect" "reflect"
"golang.org/x/tools/container/intsets" "golang.org/x/tools/container/intsets"
"golang.org/x/tools/go/types"
) )
// A peLabel is a pointer-equivalence label: two nodes with the same // A peLabel is a pointer-equivalence label: two nodes with the same

View File

@ -19,9 +19,9 @@ package pointer
import ( import (
"fmt" "fmt"
"go/types"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
// Instances of 'intrinsic' generate analysis constraints for calls to // Instances of 'intrinsic' generate analysis constraints for calls to

View File

@ -9,10 +9,10 @@ package pointer
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"strings" "strings"
"golang.org/x/tools/go/ssa" "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, // A Label is an entity that may be pointed to by a pointer, map,

View File

@ -19,6 +19,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"os" "os"
"regexp" "regexp"
@ -31,7 +32,6 @@ import (
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -32,11 +32,11 @@ package pointer
import ( import (
"fmt" "fmt"
exact "go/constant"
"go/types"
"reflect" "reflect"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
func init() { func init() {

View File

@ -11,8 +11,7 @@ package pointer
import ( import (
"fmt" "fmt"
"go/types"
"golang.org/x/tools/go/types"
) )
type solverState struct { type solverState struct {

View File

@ -9,6 +9,7 @@ package pointer
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/types"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@ -16,7 +17,6 @@ import (
"time" "time"
"golang.org/x/tools/container/intsets" "golang.org/x/tools/container/intsets"
"golang.org/x/tools/go/types"
) )
// CanPoint reports whether the type T is pointerlike, // CanPoint reports whether the type T is pointerlike,

View File

@ -34,12 +34,11 @@ package ssa
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"os" "os"
"sync" "sync"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
) )
type opaqueType struct { 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 { if m, ok := m.(*Const); ok {
// treat make([]T, n, m) as new([m]T)[:n] // 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) at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
alloc := emitNew(fn, at, pos) alloc := emitNew(fn, at, pos)
alloc.Comment = "makeslice" alloc.Comment = "makeslice"

View File

@ -9,8 +9,10 @@ package ssa_test
import ( import (
"bytes" "bytes"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
@ -19,9 +21,6 @@ import (
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "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 } func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
@ -59,7 +58,7 @@ func main() {
// Build an SSA program from the parsed file. // Build an SSA program from the parsed file.
// Load its dependencies from gc binary export data. // 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) types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -227,7 +226,7 @@ func TestRuntimeTypes(t *testing.T) {
// Create a single-file main package. // Create a single-file main package.
// Load dependencies from gc binary export data. // 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) types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil { if err != nil {
t.Errorf("test %q: %s", test.input[:15], err) t.Errorf("test %q: %s", test.input[:15], err)

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5 // +build go1.6
package ssa package ssa
@ -10,11 +10,10 @@ package ssa
import ( import (
"fmt" "fmt"
exact "go/constant"
"go/token" "go/token"
"go/types"
"strconv" "strconv"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
) )
// NewConst returns a new constant of the specified value and type. // NewConst returns a new constant of the specified value and type.
@ -118,11 +117,13 @@ func (c *Const) IsNil() bool {
return c.Value == nil 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 // Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer. // a signed 64-bit integer.
// //
func (c *Const) Int64() int64 { func (c *Const) Int64() int64 {
switch x := c.Value; x.Kind() { switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int: case exact.Int:
if i, ok := exact.Int64Val(x); ok { if i, ok := exact.Int64Val(x); ok {
return i return i
@ -139,7 +140,7 @@ func (c *Const) Int64() int64 {
// an unsigned 64-bit integer. // an unsigned 64-bit integer.
// //
func (c *Const) Uint64() uint64 { func (c *Const) Uint64() uint64 {
switch x := c.Value; x.Kind() { switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int: case exact.Int:
if u, ok := exact.Uint64Val(x); ok { if u, ok := exact.Uint64Val(x); ok {
return u return u

171
go/ssa/const15.go Normal file
View File

@ -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)
}

View File

@ -13,10 +13,10 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"os" "os"
"sync" "sync"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -12,8 +12,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
// emitNew emits to f a new (heap Alloc) instruction allocating an // emitNew emits to f a new (heap Alloc) instruction allocating an

View File

@ -8,16 +8,16 @@ package ssa_test
import ( import (
"fmt" "fmt"
"os"
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"os"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
const hello = ` const hello = `
@ -66,7 +66,7 @@ func ExampleBuildPackage() {
// Type-check the package, load dependencies. // Type-check the package, load dependencies.
// Create and build the SSA program. // Create and build the SSA program.
hello, _, err := ssautil.BuildPackage( 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 { if err != nil {
fmt.Print(err) // type error in some package fmt.Print(err) // type error in some package
return return

View File

@ -13,11 +13,10 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"io" "io"
"os" "os"
"strings" "strings"
"golang.org/x/tools/go/types"
) )
// addEdge adds a control-flow graph edge from from to to. // addEdge adds a control-flow graph edge from from to to.

View File

@ -10,6 +10,7 @@ package interp
// external or because they use "unsafe" or "reflect" operations. // external or because they use "unsafe" or "reflect" operations.
import ( import (
"go/types"
"math" "math"
"os" "os"
"runtime" "runtime"
@ -19,7 +20,6 @@ import (
"unsafe" "unsafe"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
type externalFn func(fr *frame, args []value) value type externalFn func(fr *frame, args []value) value

View File

@ -49,12 +49,12 @@ package interp // import "golang.org/x/tools/go/ssa/interp"
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"os" "os"
"reflect" "reflect"
"runtime" "runtime"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
type continuation int type continuation int

View File

@ -12,6 +12,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/build" "go/build"
"go/types"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -22,7 +23,6 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
// Each line contains a space-separated list of $GOROOT/test/ // Each line contains a space-separated list of $GOROOT/test/

View File

@ -14,7 +14,7 @@ package interp
// concurrent map access. // concurrent map access.
import ( import (
"golang.org/x/tools/go/types" "go/types"
) )
type hashable interface { type hashable interface {

View File

@ -9,14 +9,14 @@ package interp
import ( import (
"bytes" "bytes"
"fmt" "fmt"
exact "go/constant"
"go/token" "go/token"
"go/types"
"strings" "strings"
"sync" "sync"
"unsafe" "unsafe"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
// If the target program panics, the interpreter panics with this type. // If the target program panics, the interpreter panics with this type.

View File

@ -15,11 +15,11 @@ package interp
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"reflect" "reflect"
"unsafe" "unsafe"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
type opaqueType struct { type opaqueType struct {

View File

@ -38,6 +38,7 @@ package interp
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/types"
"io" "io"
"reflect" "reflect"
"strings" "strings"
@ -45,7 +46,6 @@ import (
"unsafe" "unsafe"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -46,10 +46,9 @@ package ssa
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"math/big" "math/big"
"os" "os"
"golang.org/x/tools/go/types"
) )
// If true, perform sanity checking and show diagnostic information at // If true, perform sanity checking and show diagnostic information at

View File

@ -12,8 +12,7 @@ package ssa
import ( import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
// An lvalue represents an assignable location that may appear on the // An lvalue represents an assignable location that may appear on the

View File

@ -10,8 +10,7 @@ package ssa
import ( import (
"fmt" "fmt"
"go/types"
"golang.org/x/tools/go/types"
) )
// MethodValue returns the Function implementing method sel, building // MethodValue returns the Function implementing method sel, building

View File

@ -12,11 +12,11 @@ package ssa
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/types"
"io" "io"
"reflect" "reflect"
"sort" "sort"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -11,11 +11,10 @@ package ssa
import ( import (
"fmt" "fmt"
"go/types"
"io" "io"
"os" "os"
"strings" "strings"
"golang.org/x/tools/go/types"
) )
type sanity struct { type sanity struct {

View File

@ -15,8 +15,7 @@ package ssa
import ( import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/types"
) )
// EnclosingFunction returns the function that contains the syntax // EnclosingFunction returns the function that contains the syntax

View File

@ -11,8 +11,10 @@ package ssa_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
exact "go/constant"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
@ -20,11 +22,9 @@ import (
"testing" "testing"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
func TestObjValueLookup(t *testing.T) { func TestObjValueLookup(t *testing.T) {

View File

@ -12,11 +12,11 @@ package ssa
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"sync" "sync"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -11,10 +11,10 @@ package ssautil
import ( import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
// CreateProgram returns a new program in SSA form, given a program // CreateProgram returns a new program in SSA form, given a program

View File

@ -8,15 +8,14 @@ package ssautil_test
import ( import (
"go/ast" "go/ast"
"go/importer"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"os" "os"
"testing" "testing"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
_ "golang.org/x/tools/go/gcimporter"
) )
const hello = `package main const hello = `package main
@ -39,7 +38,7 @@ func TestBuildPackage(t *testing.T) {
} }
pkg := types.NewPackage("hello", "") 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -24,9 +24,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
// A ConstCase represents a single constant comparison. // A ConstCase represents a single constant comparison.

View File

@ -12,13 +12,12 @@ package ssa
import ( import (
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"os" "os"
"sort" "sort"
"strings" "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, // FindTests returns the list of packages that define at least one Test,

View File

@ -12,11 +12,11 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"io" "io"
"os" "os"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
) )
//// AST utilities //// AST utilities

View File

@ -24,7 +24,7 @@ package ssa
import ( import (
"fmt" "fmt"
"golang.org/x/tools/go/types" "go/types"
) )
// -- wrappers ----------------------------------------------------------- // -- wrappers -----------------------------------------------------------

View File

@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package typeutil_test package typeutil_test
import ( import (
"fmt" "fmt"
"sort"
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"sort"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package typeutil package typeutil
import "golang.org/x/tools/go/types" import "go/types"
// Dependencies returns all dependencies of the specified packages. // Dependencies returns all dependencies of the specified packages.
// //

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package typeutil_test package typeutil_test
import ( import (
@ -9,19 +11,20 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"testing" "testing"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "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) { func TestDependencies(t *testing.T) {
packages := make(map[string]*types.Package) packages := make(map[string]*types.Package)
conf := types.Config{ conf := types.Config{
Packages: packages, Importer: closure(packages),
Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
return packages[path], nil
},
} }
fset := token.NewFileSet() fset := token.NewFileSet()
@ -30,12 +33,12 @@ func TestDependencies(t *testing.T) {
// F \_C_/ // F \_C_/
// \__E_/ // \__E_/
for i, content := range []string{ for i, content := range []string{
`package A`, `package a`,
`package C; import (_ "A")`, `package c; import (_ "a")`,
`package B; import (_ "A")`, `package b; import (_ "a")`,
`package E; import (_ "C")`, `package e; import (_ "c")`,
`package D; import (_ "B"; _ "C")`, `package d; import (_ "b"; _ "c")`,
`package F; import (_ "D"; _ "E")`, `package f; import (_ "d"; _ "e")`,
} { } {
f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0) f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
if err != nil { if err != nil {
@ -51,22 +54,22 @@ func TestDependencies(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
roots, want string roots, want string
}{ }{
{"A", "A"}, {"a", "a"},
{"B", "AB"}, {"b", "ab"},
{"C", "AC"}, {"c", "ac"},
{"D", "ABCD"}, {"d", "abcd"},
{"E", "ACE"}, {"e", "ace"},
{"F", "ABCDEF"}, {"f", "abcdef"},
{"BE", "ABCE"}, {"be", "abce"},
{"EB", "ACEB"}, {"eb", "aceb"},
{"DE", "ABCDE"}, {"de", "abcde"},
{"ED", "ACEBD"}, {"ed", "acebd"},
{"EF", "ACEBDF"}, {"ef", "acebdf"},
} { } {
var pkgs []*types.Package var pkgs []*types.Package
for _, r := range test.roots { for _, r := range test.roots {
pkgs = append(pkgs, conf.Packages[string(r)]) pkgs = append(pkgs, packages[string(r)])
} }
var got string var got string
for _, p := range typeutil.Dependencies(pkgs...) { for _, p := range typeutil.Dependencies(pkgs...) {

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// Package typeutil defines various utilities for types, such as Map, // Package typeutil defines various utilities for types, such as Map,
// a mapping from types.Type to interface{} values. // a mapping from types.Type to interface{} values.
package typeutil // import "golang.org/x/tools/go/types/typeutil" 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 ( import (
"bytes" "bytes"
"fmt" "fmt"
"go/types"
"reflect" "reflect"
"golang.org/x/tools/go/types"
) )
// Map is a hash-table-based mapping from types (types.Type) to // Map is a hash-table-based mapping from types (types.Type) to

316
go/types/typeutil/map14.go Normal file
View File

@ -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 FowlerNollVo 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
}

View File

@ -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, "")
}
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package typeutil_test package typeutil_test
// TODO(adonovan): // TODO(adonovan):
@ -10,9 +12,9 @@ package typeutil_test
// (e.g. all types generated by type-checking some body of real code). // (e.g. all types generated by type-checking some body of real code).
import ( import (
"go/types"
"testing" "testing"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
// This file implements a cache of method sets. // This file implements a cache of method sets.
package typeutil package typeutil
import ( import (
"go/types"
"sync" "sync"
"golang.org/x/tools/go/types"
) )
// A MethodSetCache records the method set of each type T for which // A MethodSetCache records the method set of each type T for which

View File

@ -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
}

View File

@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5
package typeutil package typeutil
// This file defines utilities for user interfaces that display types. // 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. // IntuitiveMethodSet returns the intuitive method set of a type, T.
// //

40
go/types/typeutil/ui14.go Normal file
View File

@ -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
}

View File

@ -47,8 +47,10 @@ package analysis // import "golang.org/x/tools/godoc/analysis"
import ( import (
"fmt" "fmt"
"go/build" "go/build"
exact "go/constant"
"go/scanner" "go/scanner"
"go/token" "go/token"
"go/types"
"html" "html"
"io" "io"
"log" "log"
@ -59,12 +61,10 @@ import (
"strings" "strings"
"sync" "sync"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
) )
// -- links ------------------------------------------------------------ // -- links ------------------------------------------------------------

View File

@ -14,13 +14,13 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"log" "log"
"math/big" "math/big"
"sort" "sort"
"golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
// doCallgraph computes the CALLEES and CALLERS relations. // doCallgraph computes the CALLEES and CALLERS relations.

View File

@ -13,9 +13,9 @@ package analysis
// belong to different packages and at least one is not exported? // belong to different packages and at least one is not exported?
import ( import (
"go/types"
"sort" "sort"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -16,10 +16,10 @@ package analysis
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
) )
func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) { func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {

View File

@ -22,12 +22,12 @@ package analysis
import ( import (
"fmt" "fmt"
"go/types"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )

View File

@ -10,13 +10,13 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"sort" "sort"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -10,9 +10,9 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.5 // +build go1.6
package oracle package oracle
@ -10,15 +10,15 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"go/ast" "go/ast"
exact "go/constant"
"go/token" "go/token"
"go/types"
"log" "log"
"os" "os"
"strings" "strings"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -350,7 +350,7 @@ type describeValueResult struct {
func (r *describeValueResult) display(printf printfFunc) { func (r *describeValueResult) display(printf printfFunc) {
var prefix, suffix string var prefix, suffix string
if r.constVal != nil { 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) { switch obj := r.obj.(type) {
case *types.Func: 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()) fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Const: 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: case *types.Func:
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) 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 var val string
switch mem := mem.obj.(type) { switch mem := mem.obj.(type) {
case *types.Const: case *types.Const:
val = mem.Val().String() val = constValString(mem.Val())
case *types.TypeName: case *types.TypeName:
typ = typ.Underlying() typ = typ.Underlying()
} }
@ -767,3 +767,14 @@ func methodsToSerial(this *types.Package, methods []*types.Selection, fset *toke
} }
return jmethods 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()
}

780
oracle/describe15.go Normal file
View File

@ -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()
}

View File

@ -11,10 +11,10 @@ import (
"go/ast" "go/ast"
"go/printer" "go/printer"
"go/token" "go/token"
"go/types"
"sort" "sort"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -10,12 +10,12 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
"golang.org/x/tools/refactor/importgraph" "golang.org/x/tools/refactor/importgraph"

View File

@ -27,6 +27,7 @@ import (
"go/build" "go/build"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io" "io"
"path/filepath" "path/filepath"
@ -34,7 +35,6 @@ import (
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -10,12 +10,12 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"sort" "sort"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"sort" "sort"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
@ -17,7 +18,6 @@ import (
"golang.org/x/tools/go/pointer" "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -11,11 +11,11 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"io/ioutil" "io/ioutil"
"sort" "sort"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
"golang.org/x/tools/refactor/importgraph" "golang.org/x/tools/refactor/importgraph"
) )

View File

@ -10,8 +10,8 @@ definition of package "describe"
type cake float64 type cake float64
var global *string var global *string
func main func() func main func()
const pi untyped float = 3141/1000 const pi untyped float = 3.141
const pie cake = 1768225803696341/562949953421312 const pie cake = 3.141
-------- @describe badimport1 -------- -------- @describe badimport1 --------
@ -32,7 +32,7 @@ definition of const pi untyped float
definition of const pie cake definition of const pie cake
-------- @describe const-ref-pi -------- -------- @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 defined here
-------- @describe func-def-main -------- -------- @describe func-def-main --------
@ -120,7 +120,7 @@ definition of const localpi untyped float
definition of const localpie cake definition of const localpie cake
-------- @describe const-ref-localpi -------- -------- @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 defined here
-------- @describe type-def-T -------- -------- @describe type-def-T --------

View File

@ -10,13 +10,13 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"sort" "sort"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/oracle/serial" "golang.org/x/tools/oracle/serial"
) )

View File

@ -15,9 +15,8 @@ import (
"go/format" "go/format"
"go/printer" "go/printer"
"go/token" "go/token"
"go/types"
"os" "os"
"golang.org/x/tools/go/types"
) )
const Help = ` const Help = `

View File

@ -13,8 +13,10 @@ package eg_test
import ( import (
"bytes" "bytes"
"flag" "flag"
exact "go/constant"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -22,9 +24,7 @@ import (
"strings" "strings"
"testing" "testing"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/refactor/eg" "golang.org/x/tools/refactor/eg"
) )

View File

@ -10,13 +10,13 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"log" "log"
"os" "os"
"reflect" "reflect"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/exact" "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
) )
// matchExpr reports whether pattern x matches y. // matchExpr reports whether pattern x matches y.

View File

@ -14,6 +14,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"os" "os"
"reflect" "reflect"
"sort" "sort"
@ -21,7 +22,6 @@ import (
"strings" "strings"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
) )
// Transform applies the transformation to the specified parsed file, // Transform applies the transformation to the specified parsed file,

View File

@ -12,9 +12,9 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/refactor/satisfy" "golang.org/x/tools/refactor/satisfy"
) )

View File

@ -18,6 +18,7 @@ import (
"go/format" "go/format"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -29,7 +30,6 @@ import (
"strings" "strings"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/refactor/importgraph" "golang.org/x/tools/refactor/importgraph"
"golang.org/x/tools/refactor/satisfy" "golang.org/x/tools/refactor/satisfy"

View File

@ -17,6 +17,7 @@ import (
"go/build" "go/build"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -25,7 +26,6 @@ import (
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types"
) )
// A spec specifies an entity to rename. // A spec specifies an entity to rename.

View File

@ -8,6 +8,7 @@ package rename
import ( import (
"go/ast" "go/ast"
"go/types"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -16,7 +17,6 @@ import (
"unicode" "unicode"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
) )
func objectKind(obj types.Object) string { func objectKind(obj types.Object) string {

View File

@ -50,9 +50,9 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )