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:
parent
2477c0d578
commit
542ffc7e75
|
@ -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() {
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {},
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -11,8 +11,7 @@ package pointer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/types"
|
||||||
"golang.org/x/tools/go/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type solverState struct {
|
type solverState struct {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -24,7 +24,7 @@ package ssa
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/tools/go/types"
|
"go/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -- wrappers -----------------------------------------------------------
|
// -- wrappers -----------------------------------------------------------
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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...) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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, "")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 ------------------------------------------------------------
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 --------
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 = `
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue