diff --git a/go/ssa/ssautil/load14.go b/go/ssa/ssautil/load14.go
deleted file mode 100644
index 752a9d9a43..0000000000
--- a/go/ssa/ssautil/load14.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2015 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 ssautil
-
-// This file defines utility functions for constructing programs in SSA form.
-
-import (
- "go/ast"
- "go/token"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
-)
-
-// CreateProgram returns a new program in SSA form, given a program
-// loaded from source. An SSA package is created for each transitively
-// error-free package of lprog.
-//
-// Code for bodies of functions is not built until BuildAll() is called
-// on the result.
-//
-// mode controls diagnostics and checking during SSA construction.
-//
-func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
- prog := ssa.NewProgram(lprog.Fset, mode)
-
- for _, info := range lprog.AllPackages {
- if info.TransitivelyErrorFree {
- prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
- }
- }
-
- return prog
-}
-
-// BuildPackage builds an SSA program with IR for a single package.
-//
-// It populates pkg by type-checking the specified file ASTs. All
-// dependencies are loaded using the importer specified by tc, which
-// typically loads compiler export data; SSA code cannot be built for
-// those packages. BuildPackage then constructs an ssa.Program with all
-// dependency packages created, and builds and returns the SSA package
-// corresponding to pkg.
-//
-// The caller must have set pkg.Path() to the import path.
-//
-// The operation fails if there were any type-checking or import errors.
-//
-// See ../ssa/example_test.go for an example.
-//
-func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
- if fset == nil {
- panic("no token.FileSet")
- }
- if pkg.Path() == "" {
- panic("package has no import path")
- }
-
- info := &types.Info{
- Types: make(map[ast.Expr]types.TypeAndValue),
- Defs: make(map[*ast.Ident]types.Object),
- Uses: make(map[*ast.Ident]types.Object),
- Implicits: make(map[ast.Node]types.Object),
- Scopes: make(map[ast.Node]*types.Scope),
- Selections: make(map[*ast.SelectorExpr]*types.Selection),
- }
- if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
- return nil, nil, err
- }
-
- prog := ssa.NewProgram(fset, mode)
-
- // Create SSA packages for all imports.
- // Order is not significant.
- created := make(map[*types.Package]bool)
- var createAll func(pkgs []*types.Package)
- createAll = func(pkgs []*types.Package) {
- for _, p := range pkgs {
- if !created[p] {
- created[p] = true
- prog.CreatePackage(p, nil, nil, true)
- createAll(p.Imports())
- }
- }
- }
- createAll(pkg.Imports())
-
- // Create and build the primary package.
- ssapkg := prog.CreatePackage(pkg, files, info, false)
- ssapkg.Build()
- return ssapkg, info, nil
-}
diff --git a/go/ssa/ssautil/load14_test.go b/go/ssa/ssautil/load14_test.go
deleted file mode 100644
index 41955ec508..0000000000
--- a/go/ssa/ssautil/load14_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2015 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 ssautil_test
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- "os"
- "testing"
-
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
-)
-
-const hello = `package main
-
-import "fmt"
-
-func main() {
- fmt.Println("Hello, world")
-}
-`
-
-func TestBuildPackage(t *testing.T) {
- // There is a more substantial test of BuildPackage and the
- // SSA program it builds in ../ssa/builder_test.go.
-
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "hello.go", hello, 0)
- if err != nil {
- t.Fatal(err)
- }
-
- pkg := types.NewPackage("hello", "")
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
- if err != nil {
- t.Fatal(err)
- }
- if pkg.Name() != "main" {
- t.Errorf("pkg.Name() = %s, want main", pkg.Name())
- }
- if ssapkg.Func("main") == nil {
- ssapkg.WriteTo(os.Stderr)
- t.Errorf("ssapkg has no main function")
- }
-}
-
-func TestBuildPackage_MissingImport(t *testing.T) {
- fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, "bad.go", `package bad; import "missing"`, 0)
- if err != nil {
- t.Fatal(err)
- }
-
- pkg := types.NewPackage("bad", "")
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
- if err == nil || ssapkg != nil {
- t.Fatal("BuildPackage succeeded unexpectedly")
- }
-}
diff --git a/go/ssa/ssautil/switch14.go b/go/ssa/ssautil/switch14.go
deleted file mode 100644
index b2f7f21e92..0000000000
--- a/go/ssa/ssautil/switch14.go
+++ /dev/null
@@ -1,236 +0,0 @@
-// 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
-
-package ssautil
-
-// This file implements discovery of switch and type-switch constructs
-// from low-level control flow.
-//
-// Many techniques exist for compiling a high-level switch with
-// constant cases to efficient machine code. The optimal choice will
-// depend on the data type, the specific case values, the code in the
-// body of each case, and the hardware.
-// Some examples:
-// - a lookup table (for a switch that maps constants to constants)
-// - a computed goto
-// - a binary tree
-// - a perfect hash
-// - a two-level switch (to partition constant strings by their first byte).
-
-import (
- "bytes"
- "fmt"
- "go/token"
-
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
-)
-
-// A ConstCase represents a single constant comparison.
-// It is part of a Switch.
-type ConstCase struct {
- Block *ssa.BasicBlock // block performing the comparison
- Body *ssa.BasicBlock // body of the case
- Value *ssa.Const // case comparand
-}
-
-// A TypeCase represents a single type assertion.
-// It is part of a Switch.
-type TypeCase struct {
- Block *ssa.BasicBlock // block performing the type assert
- Body *ssa.BasicBlock // body of the case
- Type types.Type // case type
- Binding ssa.Value // value bound by this case
-}
-
-// A Switch is a logical high-level control flow operation
-// (a multiway branch) discovered by analysis of a CFG containing
-// only if/else chains. It is not part of the ssa.Instruction set.
-//
-// One of ConstCases and TypeCases has length >= 2;
-// the other is nil.
-//
-// In a value switch, the list of cases may contain duplicate constants.
-// A type switch may contain duplicate types, or types assignable
-// to an interface type also in the list.
-// TODO(adonovan): eliminate such duplicates.
-//
-type Switch struct {
- Start *ssa.BasicBlock // block containing start of if/else chain
- X ssa.Value // the switch operand
- ConstCases []ConstCase // ordered list of constant comparisons
- TypeCases []TypeCase // ordered list of type assertions
- Default *ssa.BasicBlock // successor if all comparisons fail
-}
-
-func (sw *Switch) String() string {
- // We represent each block by the String() of its
- // first Instruction, e.g. "print(42:int)".
- var buf bytes.Buffer
- if sw.ConstCases != nil {
- fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
- for _, c := range sw.ConstCases {
- fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
- }
- } else {
- fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
- for _, c := range sw.TypeCases {
- fmt.Fprintf(&buf, "case %s %s: %s\n",
- c.Binding.Name(), c.Type, c.Body.Instrs[0])
- }
- }
- if sw.Default != nil {
- fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
- }
- fmt.Fprintf(&buf, "}")
- return buf.String()
-}
-
-// Switches examines the control-flow graph of fn and returns the
-// set of inferred value and type switches. A value switch tests an
-// ssa.Value for equality against two or more compile-time constant
-// values. Switches involving link-time constants (addresses) are
-// ignored. A type switch type-asserts an ssa.Value against two or
-// more types.
-//
-// The switches are returned in dominance order.
-//
-// The resulting switches do not necessarily correspond to uses of the
-// 'switch' keyword in the source: for example, a single source-level
-// switch statement with non-constant cases may result in zero, one or
-// many Switches, one per plural sequence of constant cases.
-// Switches may even be inferred from if/else- or goto-based control flow.
-// (In general, the control flow constructs of the source program
-// cannot be faithfully reproduced from the SSA representation.)
-//
-func Switches(fn *ssa.Function) []Switch {
- // Traverse the CFG in dominance order, so we don't
- // enter an if/else-chain in the middle.
- var switches []Switch
- seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
- for _, b := range fn.DomPreorder() {
- if x, k := isComparisonBlock(b); x != nil {
- // Block b starts a switch.
- sw := Switch{Start: b, X: x}
- valueSwitch(&sw, k, seen)
- if len(sw.ConstCases) > 1 {
- switches = append(switches, sw)
- }
- }
-
- if y, x, T := isTypeAssertBlock(b); y != nil {
- // Block b starts a type switch.
- sw := Switch{Start: b, X: x}
- typeSwitch(&sw, y, T, seen)
- if len(sw.TypeCases) > 1 {
- switches = append(switches, sw)
- }
- }
- }
- return switches
-}
-
-func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
- b := sw.Start
- x := sw.X
- for x == sw.X {
- if seen[b] {
- break
- }
- seen[b] = true
-
- sw.ConstCases = append(sw.ConstCases, ConstCase{
- Block: b,
- Body: b.Succs[0],
- Value: k,
- })
- b = b.Succs[1]
- if len(b.Instrs) > 2 {
- // Block b contains not just 'if x == k',
- // so it may have side effects that
- // make it unsafe to elide.
- break
- }
- if len(b.Preds) != 1 {
- // Block b has multiple predecessors,
- // so it cannot be treated as a case.
- break
- }
- x, k = isComparisonBlock(b)
- }
- sw.Default = b
-}
-
-func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
- b := sw.Start
- x := sw.X
- for x == sw.X {
- if seen[b] {
- break
- }
- seen[b] = true
-
- sw.TypeCases = append(sw.TypeCases, TypeCase{
- Block: b,
- Body: b.Succs[0],
- Type: T,
- Binding: y,
- })
- b = b.Succs[1]
- if len(b.Instrs) > 4 {
- // Block b contains not just
- // {TypeAssert; Extract #0; Extract #1; If}
- // so it may have side effects that
- // make it unsafe to elide.
- break
- }
- if len(b.Preds) != 1 {
- // Block b has multiple predecessors,
- // so it cannot be treated as a case.
- break
- }
- y, x, T = isTypeAssertBlock(b)
- }
- sw.Default = b
-}
-
-// isComparisonBlock returns the operands (v, k) if a block ends with
-// a comparison v==k, where k is a compile-time constant.
-//
-func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
- if n := len(b.Instrs); n >= 2 {
- if i, ok := b.Instrs[n-1].(*ssa.If); ok {
- if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
- if k, ok := binop.Y.(*ssa.Const); ok {
- return binop.X, k
- }
- if k, ok := binop.X.(*ssa.Const); ok {
- return binop.Y, k
- }
- }
- }
- }
- return
-}
-
-// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
-// a type assertion "if y, ok := x.(T); ok {".
-//
-func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
- if n := len(b.Instrs); n >= 4 {
- if i, ok := b.Instrs[n-1].(*ssa.If); ok {
- if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
- if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
- // hack: relies upon instruction ordering.
- if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
- return ext0, ta.X, ta.AssertedType
- }
- }
- }
- }
- }
- return
-}
diff --git a/godoc/analysis/analysis14.go b/godoc/analysis/analysis14.go
deleted file mode 100644
index ca35b41086..0000000000
--- a/godoc/analysis/analysis14.go
+++ /dev/null
@@ -1,622 +0,0 @@
-// 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 analysis performs type and pointer analysis
-// and generates mark-up for the Go source view.
-//
-// The Run method populates a Result object by running type and
-// (optionally) pointer analysis. The Result object is thread-safe
-// and at all times may be accessed by a serving thread, even as it is
-// progressively populated as analysis facts are derived.
-//
-// The Result is a mapping from each godoc file URL
-// (e.g. /src/fmt/print.go) to information about that file. The
-// information is a list of HTML markup links and a JSON array of
-// structured data values. Some of the links call client-side
-// JavaScript functions that index this array.
-//
-// The analysis computes mark-up for the following relations:
-//
-// IMPORTS: for each ast.ImportSpec, the package that it denotes.
-//
-// RESOLUTION: for each ast.Ident, its kind and type, and the location
-// of its definition.
-//
-// METHOD SETS, IMPLEMENTS: for each ast.Ident defining a named type,
-// its method-set, the set of interfaces it implements or is
-// implemented by, and its size/align values.
-//
-// CALLERS, CALLEES: for each function declaration ('func' token), its
-// callers, and for each call-site ('(' token), its callees.
-//
-// CALLGRAPH: the package docs include an interactive viewer for the
-// intra-package call graph of "fmt".
-//
-// CHANNEL PEERS: for each channel operation make/<-/close, the set of
-// other channel ops that alias the same channel(s).
-//
-// ERRORS: for each locus of a frontend (scanner/parser/type) error, the
-// location is highlighted in red and hover text provides the compiler
-// error message.
-//
-package analysis // import "golang.org/x/tools/godoc/analysis"
-
-import (
- "fmt"
- "go/build"
- "go/scanner"
- "go/token"
- "html"
- "io"
- "log"
- "os"
- "path/filepath"
- "runtime"
- "sort"
- "strings"
- "sync"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-)
-
-// -- links ------------------------------------------------------------
-
-// A Link is an HTML decoration of the bytes [Start, End) of a file.
-// Write is called before/after those bytes to emit the mark-up.
-type Link interface {
- Start() int
- End() int
- Write(w io.Writer, _ int, start bool) // the godoc.LinkWriter signature
-}
-
-// An element.
-type aLink struct {
- start, end int // =godoc.Segment
- title string // hover text
- onclick string // JS code (NB: trusted)
- href string // URL (NB: trusted)
-}
-
-func (a aLink) Start() int { return a.start }
-func (a aLink) End() int { return a.end }
-func (a aLink) Write(w io.Writer, _ int, start bool) {
- if start {
- fmt.Fprintf(w, `")
- } else {
- fmt.Fprintf(w, "")
- }
-}
-
-// An element.
-type errorLink struct {
- start int
- msg string
-}
-
-func (e errorLink) Start() int { return e.start }
-func (e errorLink) End() int { return e.start + 1 }
-
-func (e errorLink) Write(w io.Writer, _ int, start bool) {
- // causes havoc, not sure why, so use .
- if start {
- fmt.Fprintf(w, ``, html.EscapeString(e.msg))
- } else {
- fmt.Fprintf(w, "")
- }
-}
-
-// -- fileInfo ---------------------------------------------------------
-
-// FileInfo holds analysis information for the source file view.
-// Clients must not mutate it.
-type FileInfo struct {
- Data []interface{} // JSON serializable values
- Links []Link // HTML link markup
-}
-
-// A fileInfo is the server's store of hyperlinks and JSON data for a
-// particular file.
-type fileInfo struct {
- mu sync.Mutex
- data []interface{} // JSON objects
- links []Link
- sorted bool
- hasErrors bool // TODO(adonovan): surface this in the UI
-}
-
-// addLink adds a link to the Go source file fi.
-func (fi *fileInfo) addLink(link Link) {
- fi.mu.Lock()
- fi.links = append(fi.links, link)
- fi.sorted = false
- if _, ok := link.(errorLink); ok {
- fi.hasErrors = true
- }
- fi.mu.Unlock()
-}
-
-// addData adds the structured value x to the JSON data for the Go
-// source file fi. Its index is returned.
-func (fi *fileInfo) addData(x interface{}) int {
- fi.mu.Lock()
- index := len(fi.data)
- fi.data = append(fi.data, x)
- fi.mu.Unlock()
- return index
-}
-
-// get returns the file info in external form.
-// Callers must not mutate its fields.
-func (fi *fileInfo) get() FileInfo {
- var r FileInfo
- // Copy slices, to avoid races.
- fi.mu.Lock()
- r.Data = append(r.Data, fi.data...)
- if !fi.sorted {
- sort.Sort(linksByStart(fi.links))
- fi.sorted = true
- }
- r.Links = append(r.Links, fi.links...)
- fi.mu.Unlock()
- return r
-}
-
-// PackageInfo holds analysis information for the package view.
-// Clients must not mutate it.
-type PackageInfo struct {
- CallGraph []*PCGNodeJSON
- CallGraphIndex map[string]int
- Types []*TypeInfoJSON
-}
-
-type pkgInfo struct {
- mu sync.Mutex
- callGraph []*PCGNodeJSON
- callGraphIndex map[string]int // keys are (*ssa.Function).RelString()
- types []*TypeInfoJSON // type info for exported types
-}
-
-func (pi *pkgInfo) setCallGraph(callGraph []*PCGNodeJSON, callGraphIndex map[string]int) {
- pi.mu.Lock()
- pi.callGraph = callGraph
- pi.callGraphIndex = callGraphIndex
- pi.mu.Unlock()
-}
-
-func (pi *pkgInfo) addType(t *TypeInfoJSON) {
- pi.mu.Lock()
- pi.types = append(pi.types, t)
- pi.mu.Unlock()
-}
-
-// get returns the package info in external form.
-// Callers must not mutate its fields.
-func (pi *pkgInfo) get() PackageInfo {
- var r PackageInfo
- // Copy slices, to avoid races.
- pi.mu.Lock()
- r.CallGraph = append(r.CallGraph, pi.callGraph...)
- r.CallGraphIndex = pi.callGraphIndex
- r.Types = append(r.Types, pi.types...)
- pi.mu.Unlock()
- return r
-}
-
-// -- Result -----------------------------------------------------------
-
-// Result contains the results of analysis.
-// The result contains a mapping from filenames to a set of HTML links
-// and JavaScript data referenced by the links.
-type Result struct {
- mu sync.Mutex // guards maps (but not their contents)
- status string // global analysis status
- fileInfos map[string]*fileInfo // keys are godoc file URLs
- pkgInfos map[string]*pkgInfo // keys are import paths
-}
-
-// fileInfo returns the fileInfo for the specified godoc file URL,
-// constructing it as needed. Thread-safe.
-func (res *Result) fileInfo(url string) *fileInfo {
- res.mu.Lock()
- fi, ok := res.fileInfos[url]
- if !ok {
- if res.fileInfos == nil {
- res.fileInfos = make(map[string]*fileInfo)
- }
- fi = new(fileInfo)
- res.fileInfos[url] = fi
- }
- res.mu.Unlock()
- return fi
-}
-
-// Status returns a human-readable description of the current analysis status.
-func (res *Result) Status() string {
- res.mu.Lock()
- defer res.mu.Unlock()
- return res.status
-}
-
-func (res *Result) setStatusf(format string, args ...interface{}) {
- res.mu.Lock()
- res.status = fmt.Sprintf(format, args...)
- log.Printf(format, args...)
- res.mu.Unlock()
-}
-
-// FileInfo returns new slices containing opaque JSON values and the
-// HTML link markup for the specified godoc file URL. Thread-safe.
-// Callers must not mutate the elements.
-// It returns "zero" if no data is available.
-//
-func (res *Result) FileInfo(url string) (fi FileInfo) {
- return res.fileInfo(url).get()
-}
-
-// pkgInfo returns the pkgInfo for the specified import path,
-// constructing it as needed. Thread-safe.
-func (res *Result) pkgInfo(importPath string) *pkgInfo {
- res.mu.Lock()
- pi, ok := res.pkgInfos[importPath]
- if !ok {
- if res.pkgInfos == nil {
- res.pkgInfos = make(map[string]*pkgInfo)
- }
- pi = new(pkgInfo)
- res.pkgInfos[importPath] = pi
- }
- res.mu.Unlock()
- return pi
-}
-
-// PackageInfo returns new slices of JSON values for the callgraph and
-// type info for the specified package. Thread-safe.
-// Callers must not mutate its fields.
-// PackageInfo returns "zero" if no data is available.
-//
-func (res *Result) PackageInfo(importPath string) PackageInfo {
- return res.pkgInfo(importPath).get()
-}
-
-// -- analysis ---------------------------------------------------------
-
-type analysis struct {
- result *Result
- prog *ssa.Program
- ops []chanOp // all channel ops in program
- allNamed []*types.Named // all named types in the program
- ptaConfig pointer.Config
- path2url map[string]string // maps openable path to godoc file URL (/src/fmt/print.go)
- pcgs map[*ssa.Package]*packageCallGraph
-}
-
-// fileAndOffset returns the file and offset for a given pos.
-func (a *analysis) fileAndOffset(pos token.Pos) (fi *fileInfo, offset int) {
- return a.fileAndOffsetPosn(a.prog.Fset.Position(pos))
-}
-
-// fileAndOffsetPosn returns the file and offset for a given position.
-func (a *analysis) fileAndOffsetPosn(posn token.Position) (fi *fileInfo, offset int) {
- url := a.path2url[posn.Filename]
- return a.result.fileInfo(url), posn.Offset
-}
-
-// posURL returns the URL of the source extent [pos, pos+len).
-func (a *analysis) posURL(pos token.Pos, len int) string {
- if pos == token.NoPos {
- return ""
- }
- posn := a.prog.Fset.Position(pos)
- url := a.path2url[posn.Filename]
- return fmt.Sprintf("%s?s=%d:%d#L%d",
- url, posn.Offset, posn.Offset+len, posn.Line)
-}
-
-// ----------------------------------------------------------------------
-
-// Run runs program analysis and computes the resulting markup,
-// populating *result in a thread-safe manner, first with type
-// information then later with pointer analysis information if
-// enabled by the pta flag.
-//
-func Run(pta bool, result *Result) {
- conf := loader.Config{
- AllowErrors: true,
- }
-
- // Silence the default error handler.
- // Don't print all errors; we'll report just
- // one per errant package later.
- conf.TypeChecker.Error = func(e error) {}
-
- var roots, args []string // roots[i] ends with os.PathSeparator
-
- // Enumerate packages in $GOROOT.
- root := filepath.Join(runtime.GOROOT(), "src") + string(os.PathSeparator)
- roots = append(roots, root)
- args = allPackages(root)
- log.Printf("GOROOT=%s: %s\n", root, args)
-
- // Enumerate packages in $GOPATH.
- for i, dir := range filepath.SplitList(build.Default.GOPATH) {
- root := filepath.Join(dir, "src") + string(os.PathSeparator)
- roots = append(roots, root)
- pkgs := allPackages(root)
- log.Printf("GOPATH[%d]=%s: %s\n", i, root, pkgs)
- args = append(args, pkgs...)
- }
-
- // Uncomment to make startup quicker during debugging.
- //args = []string{"golang.org/x/tools/cmd/godoc"}
- //args = []string{"fmt"}
-
- if _, err := conf.FromArgs(args, true); err != nil {
- // TODO(adonovan): degrade gracefully, not fail totally.
- // (The crippling case is a parse error in an external test file.)
- result.setStatusf("Analysis failed: %s.", err) // import error
- return
- }
-
- result.setStatusf("Loading and type-checking packages...")
- iprog, err := conf.Load()
- if iprog != nil {
- // Report only the first error of each package.
- for _, info := range iprog.AllPackages {
- for _, err := range info.Errors {
- fmt.Fprintln(os.Stderr, err)
- break
- }
- }
- log.Printf("Loaded %d packages.", len(iprog.AllPackages))
- }
- if err != nil {
- result.setStatusf("Loading failed: %s.\n", err)
- return
- }
-
- // Create SSA-form program representation.
- // Only the transitively error-free packages are used.
- prog := ssautil.CreateProgram(iprog, ssa.GlobalDebug)
-
- // Compute the set of main packages, including testmain.
- allPackages := prog.AllPackages()
- var mainPkgs []*ssa.Package
- if testmain := prog.CreateTestMainPackage(allPackages...); testmain != nil {
- mainPkgs = append(mainPkgs, testmain)
- if p := testmain.Const("packages"); p != nil {
- log.Printf("Tested packages: %v", exact.StringVal(p.Value.Value))
- }
- }
- for _, pkg := range allPackages {
- if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil {
- mainPkgs = append(mainPkgs, pkg)
- }
- }
- log.Print("Transitively error-free main packages: ", mainPkgs)
-
- // Build SSA code for bodies of all functions in the whole program.
- result.setStatusf("Constructing SSA form...")
- prog.Build()
- log.Print("SSA construction complete")
-
- a := analysis{
- result: result,
- prog: prog,
- pcgs: make(map[*ssa.Package]*packageCallGraph),
- }
-
- // Build a mapping from openable filenames to godoc file URLs,
- // i.e. "/src/" plus path relative to GOROOT/src or GOPATH[i]/src.
- a.path2url = make(map[string]string)
- for _, info := range iprog.AllPackages {
- nextfile:
- for _, f := range info.Files {
- if f.Pos() == 0 {
- continue // e.g. files generated by cgo
- }
- abs := iprog.Fset.File(f.Pos()).Name()
- // Find the root to which this file belongs.
- for _, root := range roots {
- rel := strings.TrimPrefix(abs, root)
- if len(rel) < len(abs) {
- a.path2url[abs] = "/src/" + filepath.ToSlash(rel)
- continue nextfile
- }
- }
-
- log.Printf("Can't locate file %s (package %q) beneath any root",
- abs, info.Pkg.Path())
- }
- }
-
- // Add links for scanner, parser, type-checker errors.
- // TODO(adonovan): fix: these links can overlap with
- // identifier markup, causing the renderer to emit some
- // characters twice.
- errors := make(map[token.Position][]string)
- for _, info := range iprog.AllPackages {
- for _, err := range info.Errors {
- switch err := err.(type) {
- case types.Error:
- posn := a.prog.Fset.Position(err.Pos)
- errors[posn] = append(errors[posn], err.Msg)
- case scanner.ErrorList:
- for _, e := range err {
- errors[e.Pos] = append(errors[e.Pos], e.Msg)
- }
- default:
- log.Printf("Package %q has error (%T) without position: %v\n",
- info.Pkg.Path(), err, err)
- }
- }
- }
- for posn, errs := range errors {
- fi, offset := a.fileAndOffsetPosn(posn)
- fi.addLink(errorLink{
- start: offset,
- msg: strings.Join(errs, "\n"),
- })
- }
-
- // ---------- type-based analyses ----------
-
- // Compute the all-pairs IMPLEMENTS relation.
- // Collect all named types, even local types
- // (which can have methods via promotion)
- // and the built-in "error".
- errorType := types.Universe.Lookup("error").Type().(*types.Named)
- a.allNamed = append(a.allNamed, errorType)
- for _, info := range iprog.AllPackages {
- for _, obj := range info.Defs {
- if obj, ok := obj.(*types.TypeName); ok {
- a.allNamed = append(a.allNamed, obj.Type().(*types.Named))
- }
- }
- }
- log.Print("Computing implements relation...")
- facts := computeImplements(&a.prog.MethodSets, a.allNamed)
-
- // Add the type-based analysis results.
- log.Print("Extracting type info...")
- for _, info := range iprog.AllPackages {
- a.doTypeInfo(info, facts)
- }
-
- a.visitInstrs(pta)
-
- result.setStatusf("Type analysis complete.")
-
- if pta {
- a.pointer(mainPkgs)
- }
-}
-
-// visitInstrs visits all SSA instructions in the program.
-func (a *analysis) visitInstrs(pta bool) {
- log.Print("Visit instructions...")
- for fn := range ssautil.AllFunctions(a.prog) {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- // CALLEES (static)
- // (Dynamic calls require pointer analysis.)
- //
- // We use the SSA representation to find the static callee,
- // since in many cases it does better than the
- // types.Info.{Refs,Selection} information. For example:
- //
- // defer func(){}() // static call to anon function
- // f := func(){}; f() // static call to anon function
- // f := fmt.Println; f() // static call to named function
- //
- // The downside is that we get no static callee information
- // for packages that (transitively) contain errors.
- if site, ok := instr.(ssa.CallInstruction); ok {
- if callee := site.Common().StaticCallee(); callee != nil {
- // TODO(adonovan): callgraph: elide wrappers.
- // (Do static calls ever go to wrappers?)
- if site.Common().Pos() != token.NoPos {
- a.addCallees(site, []*ssa.Function{callee})
- }
- }
- }
-
- if !pta {
- continue
- }
-
- // CHANNEL PEERS
- // Collect send/receive/close instructions in the whole ssa.Program.
- for _, op := range chanOps(instr) {
- a.ops = append(a.ops, op)
- a.ptaConfig.AddQuery(op.ch) // add channel ssa.Value to PTA query
- }
- }
- }
- }
- log.Print("Visit instructions complete")
-}
-
-// pointer runs the pointer analysis.
-func (a *analysis) pointer(mainPkgs []*ssa.Package) {
- // Run the pointer analysis and build the complete callgraph.
- a.ptaConfig.Mains = mainPkgs
- a.ptaConfig.BuildCallGraph = true
- a.ptaConfig.Reflection = false // (for now)
-
- a.result.setStatusf("Pointer analysis running...")
-
- ptares, err := pointer.Analyze(&a.ptaConfig)
- if err != nil {
- // If this happens, it indicates a bug.
- a.result.setStatusf("Pointer analysis failed: %s.", err)
- return
- }
- log.Print("Pointer analysis complete.")
-
- // Add the results of pointer analysis.
-
- a.result.setStatusf("Computing channel peers...")
- a.doChannelPeers(ptares.Queries)
- a.result.setStatusf("Computing dynamic call graph edges...")
- a.doCallgraph(ptares.CallGraph)
-
- a.result.setStatusf("Analysis complete.")
-}
-
-type linksByStart []Link
-
-func (a linksByStart) Less(i, j int) bool { return a[i].Start() < a[j].Start() }
-func (a linksByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a linksByStart) Len() int { return len(a) }
-
-// allPackages returns a new sorted slice of all packages beneath the
-// specified package root directory, e.g. $GOROOT/src or $GOPATH/src.
-// Derived from from go/ssa/stdlib_test.go
-// root must end with os.PathSeparator.
-//
-// TODO(adonovan): use buildutil.AllPackages when the tree thaws.
-func allPackages(root string) []string {
- var pkgs []string
- filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
- if info == nil {
- return nil // non-existent root directory?
- }
- if !info.IsDir() {
- return nil // not a directory
- }
- // Prune the search if we encounter any of these names:
- base := filepath.Base(path)
- if base == "testdata" || strings.HasPrefix(base, ".") {
- return filepath.SkipDir
- }
- pkg := filepath.ToSlash(strings.TrimPrefix(path, root))
- switch pkg {
- case "builtin":
- return filepath.SkipDir
- case "":
- return nil // ignore root of tree
- }
- pkgs = append(pkgs, pkg)
- return nil
- })
- return pkgs
-}
diff --git a/godoc/analysis/callgraph14.go b/godoc/analysis/callgraph14.go
deleted file mode 100644
index 2692d7e7b9..0000000000
--- a/godoc/analysis/callgraph14.go
+++ /dev/null
@@ -1,353 +0,0 @@
-// 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 analysis
-
-// This file computes the CALLERS and CALLEES relations from the call
-// graph. CALLERS/CALLEES information is displayed in the lower pane
-// when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "log"
- "math/big"
- "sort"
-
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
-)
-
-// doCallgraph computes the CALLEES and CALLERS relations.
-func (a *analysis) doCallgraph(cg *callgraph.Graph) {
- log.Print("Deleting synthetic nodes...")
- // TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
- // inefficient and can be (unpredictably) slow.
- cg.DeleteSyntheticNodes()
- log.Print("Synthetic nodes deleted")
-
- // Populate nodes of package call graphs (PCGs).
- for _, n := range cg.Nodes {
- a.pcgAddNode(n.Func)
- }
- // Within each PCG, sort funcs by name.
- for _, pcg := range a.pcgs {
- pcg.sortNodes()
- }
-
- calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
- callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
- for _, n := range cg.Nodes {
- for _, e := range n.Out {
- if e.Site == nil {
- continue // a call from a synthetic node such as
- }
-
- // Add (site pos, callee) to calledFuncs.
- // (Dynamic calls only.)
- callee := e.Callee.Func
-
- a.pcgAddEdge(n.Func, callee)
-
- if callee.Synthetic != "" {
- continue // call of a package initializer
- }
-
- if e.Site.Common().StaticCallee() == nil {
- // dynamic call
- // (CALLEES information for static calls
- // is computed using SSA information.)
- lparen := e.Site.Common().Pos()
- if lparen != token.NoPos {
- fns := calledFuncs[e.Site]
- if fns == nil {
- fns = make(map[*ssa.Function]bool)
- calledFuncs[e.Site] = fns
- }
- fns[callee] = true
- }
- }
-
- // Add (callee, site) to callingSites.
- fns := callingSites[callee]
- if fns == nil {
- fns = make(map[ssa.CallInstruction]bool)
- callingSites[callee] = fns
- }
- fns[e.Site] = true
- }
- }
-
- // CALLEES.
- log.Print("Callees...")
- for site, fns := range calledFuncs {
- var funcs funcsByPos
- for fn := range fns {
- funcs = append(funcs, fn)
- }
- sort.Sort(funcs)
-
- a.addCallees(site, funcs)
- }
-
- // CALLERS
- log.Print("Callers...")
- for callee, sites := range callingSites {
- pos := funcToken(callee)
- if pos == token.NoPos {
- log.Printf("CALLERS: skipping %s: no pos", callee)
- continue
- }
-
- var this *types.Package // for relativizing names
- if callee.Pkg != nil {
- this = callee.Pkg.Pkg
- }
-
- // Compute sites grouped by parent, with text and URLs.
- sitesByParent := make(map[*ssa.Function]sitesByPos)
- for site := range sites {
- fn := site.Parent()
- sitesByParent[fn] = append(sitesByParent[fn], site)
- }
- var funcs funcsByPos
- for fn := range sitesByParent {
- funcs = append(funcs, fn)
- }
- sort.Sort(funcs)
-
- v := callersJSON{
- Callee: callee.String(),
- Callers: []callerJSON{}, // (JS wants non-nil)
- }
- for _, fn := range funcs {
- caller := callerJSON{
- Func: prettyFunc(this, fn),
- Sites: []anchorJSON{}, // (JS wants non-nil)
- }
- sites := sitesByParent[fn]
- sort.Sort(sites)
- for _, site := range sites {
- pos := site.Common().Pos()
- if pos != token.NoPos {
- caller.Sites = append(caller.Sites, anchorJSON{
- Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
- Href: a.posURL(pos, len("(")),
- })
- }
- }
- v.Callers = append(v.Callers, caller)
- }
-
- fi, offset := a.fileAndOffset(pos)
- fi.addLink(aLink{
- start: offset,
- end: offset + len("func"),
- title: fmt.Sprintf("%d callers", len(sites)),
- onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
- })
- }
-
- // PACKAGE CALLGRAPH
- log.Print("Package call graph...")
- for pkg, pcg := range a.pcgs {
- // Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
- index := make(map[string]int)
-
- // Treat exported functions (and exported methods of
- // exported named types) as roots even if they aren't
- // actually called from outside the package.
- for i, n := range pcg.nodes {
- if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
- continue
- }
- recv := n.fn.Signature.Recv()
- if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
- roots := &pcg.nodes[0].edges
- roots.SetBit(roots, i, 1)
- }
- index[n.fn.RelString(pkg.Pkg)] = i
- }
-
- json := a.pcgJSON(pcg)
-
- // TODO(adonovan): pkg.Path() is not unique!
- // It is possible to declare a non-test package called x_test.
- a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
- }
-}
-
-// addCallees adds client data and links for the facts that site calls fns.
-func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
- v := calleesJSON{
- Descr: site.Common().Description(),
- Callees: []anchorJSON{}, // (JS wants non-nil)
- }
- var this *types.Package // for relativizing names
- if p := site.Parent().Package(); p != nil {
- this = p.Pkg
- }
-
- for _, fn := range fns {
- v.Callees = append(v.Callees, anchorJSON{
- Text: prettyFunc(this, fn),
- Href: a.posURL(funcToken(fn), len("func")),
- })
- }
-
- fi, offset := a.fileAndOffset(site.Common().Pos())
- fi.addLink(aLink{
- start: offset,
- end: offset + len("("),
- title: fmt.Sprintf("%d callees", len(v.Callees)),
- onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
- })
-}
-
-// -- utilities --------------------------------------------------------
-
-// stable order within packages but undefined across packages.
-type funcsByPos []*ssa.Function
-
-func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
-func (a funcsByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a funcsByPos) Len() int { return len(a) }
-
-type sitesByPos []ssa.CallInstruction
-
-func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
-func (a sitesByPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a sitesByPos) Len() int { return len(a) }
-
-func funcToken(fn *ssa.Function) token.Pos {
- switch syntax := fn.Syntax().(type) {
- case *ast.FuncLit:
- return syntax.Type.Func
- case *ast.FuncDecl:
- return syntax.Type.Func
- }
- return token.NoPos
-}
-
-// prettyFunc pretty-prints fn for the user interface.
-// TODO(adonovan): return HTML so we have more markup freedom.
-func prettyFunc(this *types.Package, fn *ssa.Function) string {
- if fn.Parent() != nil {
- return fmt.Sprintf("%s in %s",
- types.TypeString(fn.Signature, types.RelativeTo(this)),
- prettyFunc(this, fn.Parent()))
- }
- if fn.Synthetic != "" && fn.Name() == "init" {
- // (This is the actual initializer, not a declared 'func init').
- if fn.Pkg.Pkg == this {
- return "package initializer"
- }
- return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
- }
- return fn.RelString(this)
-}
-
-// -- intra-package callgraph ------------------------------------------
-
-// pcgNode represents a node in the package call graph (PCG).
-type pcgNode struct {
- fn *ssa.Function
- pretty string // cache of prettyFunc(fn)
- edges big.Int // set of callee func indices
-}
-
-// A packageCallGraph represents the intra-package edges of the global call graph.
-// The zeroth node indicates "all external functions".
-type packageCallGraph struct {
- nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
- nodes []*pcgNode // maps node index to node
-}
-
-// sortNodes populates pcg.nodes in name order and updates the nodeIndex.
-func (pcg *packageCallGraph) sortNodes() {
- nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
- nodes = append(nodes, &pcgNode{fn: nil, pretty: ""})
- for fn := range pcg.nodeIndex {
- nodes = append(nodes, &pcgNode{
- fn: fn,
- pretty: prettyFunc(fn.Pkg.Pkg, fn),
- })
- }
- sort.Sort(pcgNodesByPretty(nodes[1:]))
- for i, n := range nodes {
- pcg.nodeIndex[n.fn] = i
- }
- pcg.nodes = nodes
-}
-
-func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
- var callerIndex int
- if caller.Pkg == callee.Pkg {
- // intra-package edge
- callerIndex = pcg.nodeIndex[caller]
- if callerIndex < 1 {
- panic(caller)
- }
- }
- edges := &pcg.nodes[callerIndex].edges
- edges.SetBit(edges, pcg.nodeIndex[callee], 1)
-}
-
-func (a *analysis) pcgAddNode(fn *ssa.Function) {
- if fn.Pkg == nil {
- return
- }
- pcg, ok := a.pcgs[fn.Pkg]
- if !ok {
- pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
- a.pcgs[fn.Pkg] = pcg
- }
- pcg.nodeIndex[fn] = -1
-}
-
-func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
- if callee.Pkg != nil {
- a.pcgs[callee.Pkg].addEdge(caller, callee)
- }
-}
-
-// pcgJSON returns a new slice of callgraph JSON values.
-func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
- var nodes []*PCGNodeJSON
- for _, n := range pcg.nodes {
-
- // TODO(adonovan): why is there no good way to iterate
- // over the set bits of a big.Int?
- var callees []int
- nbits := n.edges.BitLen()
- for j := 0; j < nbits; j++ {
- if n.edges.Bit(j) == 1 {
- callees = append(callees, j)
- }
- }
-
- var pos token.Pos
- if n.fn != nil {
- pos = funcToken(n.fn)
- }
- nodes = append(nodes, &PCGNodeJSON{
- Func: anchorJSON{
- Text: n.pretty,
- Href: a.posURL(pos, len("func")),
- },
- Callees: callees,
- })
- }
- return nodes
-}
-
-type pcgNodesByPretty []*pcgNode
-
-func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
-func (a pcgNodesByPretty) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a pcgNodesByPretty) Len() int { return len(a) }
diff --git a/godoc/analysis/implements14.go b/godoc/analysis/implements14.go
deleted file mode 100644
index 0ad10089fd..0000000000
--- a/godoc/analysis/implements14.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// 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 analysis
-
-// This file computes the "implements" relation over all pairs of
-// named types in the program. (The mark-up is done by typeinfo.go.)
-
-// TODO(adonovan): do we want to report implements(C, I) where C and I
-// belong to different packages and at least one is not exported?
-
-import (
- "sort"
-
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-// computeImplements computes the "implements" relation over all pairs
-// of named types in allNamed.
-func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
- // Information about a single type's method set.
- type msetInfo struct {
- typ types.Type
- mset *types.MethodSet
- mask1, mask2 uint64
- }
-
- initMsetInfo := func(info *msetInfo, typ types.Type) {
- info.typ = typ
- info.mset = cache.MethodSet(typ)
- for i := 0; i < info.mset.Len(); i++ {
- name := info.mset.At(i).Obj().Name()
- info.mask1 |= 1 << methodBit(name[0])
- info.mask2 |= 1 << methodBit(name[len(name)-1])
- }
- }
-
- // satisfies(T, U) reports whether type T satisfies type U.
- // U must be an interface.
- //
- // Since there are thousands of types (and thus millions of
- // pairs of types) and types.Assignable(T, U) is relatively
- // expensive, we compute assignability directly from the
- // method sets. (At least one of T and U must be an
- // interface.)
- //
- // We use a trick (thanks gri!) related to a Bloom filter to
- // quickly reject most tests, which are false. For each
- // method set, we precompute a mask, a set of bits, one per
- // distinct initial byte of each method name. Thus the mask
- // for io.ReadWriter would be {'R','W'}. AssignableTo(T, U)
- // cannot be true unless mask(T)&mask(U)==mask(U).
- //
- // As with a Bloom filter, we can improve precision by testing
- // additional hashes, e.g. using the last letter of each
- // method name, so long as the subset mask property holds.
- //
- // When analyzing the standard library, there are about 1e6
- // calls to satisfies(), of which 0.6% return true. With a
- // 1-hash filter, 95% of calls avoid the expensive check; with
- // a 2-hash filter, this grows to 98.2%.
- satisfies := func(T, U *msetInfo) bool {
- return T.mask1&U.mask1 == U.mask1 &&
- T.mask2&U.mask2 == U.mask2 &&
- containsAllIdsOf(T.mset, U.mset)
- }
-
- // Information about a named type N, and perhaps also *N.
- type namedInfo struct {
- isInterface bool
- base msetInfo // N
- ptr msetInfo // *N, iff N !isInterface
- }
-
- var infos []namedInfo
-
- // Precompute the method sets and their masks.
- for _, N := range allNamed {
- var info namedInfo
- initMsetInfo(&info.base, N)
- _, info.isInterface = N.Underlying().(*types.Interface)
- if !info.isInterface {
- initMsetInfo(&info.ptr, types.NewPointer(N))
- }
-
- if info.base.mask1|info.ptr.mask1 == 0 {
- continue // neither N nor *N has methods
- }
-
- infos = append(infos, info)
- }
-
- facts := make(map[*types.Named]implementsFacts)
-
- // Test all pairs of distinct named types (T, U).
- // TODO(adonovan): opt: compute (U, T) at the same time.
- for t := range infos {
- T := &infos[t]
- var to, from, fromPtr []types.Type
- for u := range infos {
- if t == u {
- continue
- }
- U := &infos[u]
- switch {
- case T.isInterface && U.isInterface:
- if satisfies(&U.base, &T.base) {
- to = append(to, U.base.typ)
- }
- if satisfies(&T.base, &U.base) {
- from = append(from, U.base.typ)
- }
- case T.isInterface: // U concrete
- if satisfies(&U.base, &T.base) {
- to = append(to, U.base.typ)
- } else if satisfies(&U.ptr, &T.base) {
- to = append(to, U.ptr.typ)
- }
- case U.isInterface: // T concrete
- if satisfies(&T.base, &U.base) {
- from = append(from, U.base.typ)
- } else if satisfies(&T.ptr, &U.base) {
- fromPtr = append(fromPtr, U.base.typ)
- }
- }
- }
-
- // Sort types (arbitrarily) to avoid nondeterminism.
- sort.Sort(typesByString(to))
- sort.Sort(typesByString(from))
- sort.Sort(typesByString(fromPtr))
-
- facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
- }
-
- return facts
-}
-
-type implementsFacts struct {
- to []types.Type // named or ptr-to-named types assignable to interface T
- from []types.Type // named interfaces assignable from T
- fromPtr []types.Type // named interfaces assignable only from *T
-}
-
-type typesByString []types.Type
-
-func (p typesByString) Len() int { return len(p) }
-func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
-func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-
-// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
-func methodBit(x byte) uint64 {
- switch {
- case 'a' <= x && x <= 'z':
- return uint64(x - 'a')
- case 'A' <= x && x <= 'Z':
- return uint64(26 + x - 'A')
- }
- return 52 // all other bytes
-}
-
-// containsAllIdsOf reports whether the method identifiers of T are a
-// superset of those in U. If U belongs to an interface type, the
-// result is equal to types.Assignable(T, U), but is cheaper to compute.
-//
-// TODO(gri): make this a method of *types.MethodSet.
-//
-func containsAllIdsOf(T, U *types.MethodSet) bool {
- t, tlen := 0, T.Len()
- u, ulen := 0, U.Len()
- for t < tlen && u < ulen {
- tMeth := T.At(t).Obj()
- uMeth := U.At(u).Obj()
- tId := tMeth.Id()
- uId := uMeth.Id()
- if tId > uId {
- // U has a method T lacks: fail.
- return false
- }
- if tId < uId {
- // T has a method U lacks: ignore it.
- t++
- continue
- }
- // U and T both have a method of this Id. Check types.
- if !types.Identical(tMeth.Type(), uMeth.Type()) {
- return false // type mismatch
- }
- u++
- t++
- }
- return u == ulen
-}
diff --git a/godoc/analysis/peers14.go b/godoc/analysis/peers14.go
deleted file mode 100644
index ba5e8d6308..0000000000
--- a/godoc/analysis/peers14.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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 analysis
-
-// This file computes the channel "peers" relation over all pairs of
-// channel operations in the program. The peers are displayed in the
-// lower pane when a channel operation (make, <-, close) is clicked.
-
-// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too,
-// then enable reflection in PTA.
-
-import (
- "fmt"
- "go/token"
-
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
-)
-
-func (a *analysis) doChannelPeers(ptsets map[ssa.Value]pointer.Pointer) {
- addSendRecv := func(j *commJSON, op chanOp) {
- j.Ops = append(j.Ops, commOpJSON{
- Op: anchorJSON{
- Text: op.mode,
- Href: a.posURL(op.pos, op.len),
- },
- Fn: prettyFunc(nil, op.fn),
- })
- }
-
- // Build an undirected bipartite multigraph (binary relation)
- // of MakeChan ops and send/recv/close ops.
- //
- // TODO(adonovan): opt: use channel element types to partition
- // the O(n^2) problem into subproblems.
- aliasedOps := make(map[*ssa.MakeChan][]chanOp)
- opToMakes := make(map[chanOp][]*ssa.MakeChan)
- for _, op := range a.ops {
- // Combine the PT sets from all contexts.
- var makes []*ssa.MakeChan // aliased ops
- ptr, ok := ptsets[op.ch]
- if !ok {
- continue // e.g. channel op in dead code
- }
- for _, label := range ptr.PointsTo().Labels() {
- makechan, ok := label.Value().(*ssa.MakeChan)
- if !ok {
- continue // skip intrinsically-created channels for now
- }
- if makechan.Pos() == token.NoPos {
- continue // not possible?
- }
- makes = append(makes, makechan)
- aliasedOps[makechan] = append(aliasedOps[makechan], op)
- }
- opToMakes[op] = makes
- }
-
- // Now that complete relation is built, build links for ops.
- for _, op := range a.ops {
- v := commJSON{
- Ops: []commOpJSON{}, // (JS wants non-nil)
- }
- ops := make(map[chanOp]bool)
- for _, makechan := range opToMakes[op] {
- v.Ops = append(v.Ops, commOpJSON{
- Op: anchorJSON{
- Text: "made",
- Href: a.posURL(makechan.Pos()-token.Pos(len("make")),
- len("make")),
- },
- Fn: makechan.Parent().RelString(op.fn.Package().Pkg),
- })
- for _, op := range aliasedOps[makechan] {
- ops[op] = true
- }
- }
- for op := range ops {
- addSendRecv(&v, op)
- }
-
- // Add links for each aliased op.
- fi, offset := a.fileAndOffset(op.pos)
- fi.addLink(aLink{
- start: offset,
- end: offset + op.len,
- title: "show channel ops",
- onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
- })
- }
- // Add links for makechan ops themselves.
- for makechan, ops := range aliasedOps {
- v := commJSON{
- Ops: []commOpJSON{}, // (JS wants non-nil)
- }
- for _, op := range ops {
- addSendRecv(&v, op)
- }
-
- fi, offset := a.fileAndOffset(makechan.Pos())
- fi.addLink(aLink{
- start: offset - len("make"),
- end: offset,
- title: "show channel ops",
- onclick: fmt.Sprintf("onClickComm(%d)", fi.addData(v)),
- })
- }
-}
-
-// -- utilities --------------------------------------------------------
-
-// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), close(), or a SelectState.
-// Derived from oracle/peers.go.
-type chanOp struct {
- ch ssa.Value
- mode string // sent|received|closed
- pos token.Pos
- len int
- fn *ssa.Function
-}
-
-// chanOps returns a slice of all the channel operations in the instruction.
-// Derived from oracle/peers.go.
-func chanOps(instr ssa.Instruction) []chanOp {
- fn := instr.Parent()
- var ops []chanOp
- switch instr := instr.(type) {
- case *ssa.UnOp:
- if instr.Op == token.ARROW {
- // TODO(adonovan): don't assume <-ch; could be 'range ch'.
- ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
- }
- case *ssa.Send:
- ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
- case *ssa.Select:
- for _, st := range instr.States {
- mode := "received"
- if st.Dir == types.SendOnly {
- mode = "sent"
- }
- ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
- }
- case ssa.CallInstruction:
- call := instr.Common()
- if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
- pos := instr.Common().Pos()
- ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
- }
- }
- return ops
-}
diff --git a/godoc/analysis/typeinfo14.go b/godoc/analysis/typeinfo14.go
deleted file mode 100644
index d10abcecd2..0000000000
--- a/godoc/analysis/typeinfo14.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// 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 analysis
-
-// This file computes the markup for information from go/types:
-// IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and
-// the IMPLEMENTS relation.
-//
-// IMPORTS links connect import specs to the documentation for the
-// imported package.
-//
-// RESOLUTION links referring identifiers to their defining
-// identifier, and adds tooltips for kind and type.
-//
-// METHOD SETS, size/alignment, and the IMPLEMENTS relation are
-// displayed in the lower pane when a type's defining identifier is
-// clicked.
-
-import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-// TODO(adonovan): audit to make sure it's safe on ill-typed packages.
-
-// TODO(adonovan): use same Sizes as loader.Config.
-var sizes = types.StdSizes{8, 8}
-
-func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) {
- // We must not assume the corresponding SSA packages were
- // created (i.e. were transitively error-free).
-
- // IMPORTS
- for _, f := range info.Files {
- // Package decl.
- fi, offset := a.fileAndOffset(f.Name.Pos())
- fi.addLink(aLink{
- start: offset,
- end: offset + len(f.Name.Name),
- title: "Package docs for " + info.Pkg.Path(),
- // TODO(adonovan): fix: we're putting the untrusted Path()
- // into a trusted field. What's the appropriate sanitizer?
- href: "/pkg/" + info.Pkg.Path(),
- })
-
- // Import specs.
- for _, imp := range f.Imports {
- // Remove quotes.
- L := int(imp.End()-imp.Path.Pos()) - len(`""`)
- path, _ := strconv.Unquote(imp.Path.Value)
- fi, offset := a.fileAndOffset(imp.Path.Pos())
- fi.addLink(aLink{
- start: offset + 1,
- end: offset + 1 + L,
- title: "Package docs for " + path,
- // TODO(adonovan): fix: we're putting the untrusted path
- // into a trusted field. What's the appropriate sanitizer?
- href: "/pkg/" + path,
- })
- }
- }
-
- // RESOLUTION
- qualifier := types.RelativeTo(info.Pkg)
- for id, obj := range info.Uses {
- // Position of the object definition.
- pos := obj.Pos()
- Len := len(obj.Name())
-
- // Correct the position for non-renaming import specs.
- // import "sync/atomic"
- // ^^^^^^^^^^^
- if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() {
- // Assume this is a non-renaming import.
- // NB: not true for degenerate renamings: `import foo "foo"`.
- pos++
- Len = len(obj.Imported().Path())
- }
-
- if obj.Pkg() == nil {
- continue // don't mark up built-ins.
- }
-
- fi, offset := a.fileAndOffset(id.NamePos)
- fi.addLink(aLink{
- start: offset,
- end: offset + len(id.Name),
- title: types.ObjectString(obj, qualifier),
- href: a.posURL(pos, Len),
- })
- }
-
- // IMPLEMENTS & METHOD SETS
- for _, obj := range info.Defs {
- if obj, ok := obj.(*types.TypeName); ok {
- a.namedType(obj, implements)
- }
- }
-}
-
-func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) {
- qualifier := types.RelativeTo(obj.Pkg())
- T := obj.Type().(*types.Named)
- v := &TypeInfoJSON{
- Name: obj.Name(),
- Size: sizes.Sizeof(T),
- Align: sizes.Alignof(T),
- Methods: []anchorJSON{}, // (JS wants non-nil)
- }
-
- // addFact adds the fact "is implemented by T" (by) or
- // "implements T" (!by) to group.
- addFact := func(group *implGroupJSON, T types.Type, by bool) {
- Tobj := deref(T).(*types.Named).Obj()
- var byKind string
- if by {
- // Show underlying kind of implementing type,
- // e.g. "slice", "array", "struct".
- s := reflect.TypeOf(T.Underlying()).String()
- byKind = strings.ToLower(strings.TrimPrefix(s, "*types."))
- }
- group.Facts = append(group.Facts, implFactJSON{
- ByKind: byKind,
- Other: anchorJSON{
- Href: a.posURL(Tobj.Pos(), len(Tobj.Name())),
- Text: types.TypeString(T, qualifier),
- },
- })
- }
-
- // IMPLEMENTS
- if r, ok := implements[T]; ok {
- if isInterface(T) {
- // "T is implemented by " ...
- // "T is implemented by "...
- // "T implements "...
- group := implGroupJSON{
- Descr: types.TypeString(T, qualifier),
- }
- // Show concrete types first; use two passes.
- for _, sub := range r.to {
- if !isInterface(sub) {
- addFact(&group, sub, true)
- }
- }
- for _, sub := range r.to {
- if isInterface(sub) {
- addFact(&group, sub, true)
- }
- }
- for _, super := range r.from {
- addFact(&group, super, false)
- }
- v.ImplGroups = append(v.ImplGroups, group)
- } else {
- // T is concrete.
- if r.from != nil {
- // "T implements "...
- group := implGroupJSON{
- Descr: types.TypeString(T, qualifier),
- }
- for _, super := range r.from {
- addFact(&group, super, false)
- }
- v.ImplGroups = append(v.ImplGroups, group)
- }
- if r.fromPtr != nil {
- // "*C implements "...
- group := implGroupJSON{
- Descr: "*" + types.TypeString(T, qualifier),
- }
- for _, psuper := range r.fromPtr {
- addFact(&group, psuper, false)
- }
- v.ImplGroups = append(v.ImplGroups, group)
- }
- }
- }
-
- // METHOD SETS
- for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) {
- meth := sel.Obj().(*types.Func)
- pos := meth.Pos() // may be 0 for error.Error
- v.Methods = append(v.Methods, anchorJSON{
- Href: a.posURL(pos, len(meth.Name())),
- Text: types.SelectionString(sel, qualifier),
- })
- }
-
- // Since there can be many specs per decl, we
- // can't attach the link to the keyword 'type'
- // (as we do with 'func'); we use the Ident.
- fi, offset := a.fileAndOffset(obj.Pos())
- fi.addLink(aLink{
- start: offset,
- end: offset + len(obj.Name()),
- title: fmt.Sprintf("type info for %s", obj.Name()),
- onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)),
- })
-
- // Add info for exported package-level types to the package info.
- if obj.Exported() && isPackageLevel(obj) {
- // TODO(adonovan): Path is not unique!
- // It is possible to declare a non-test package called x_test.
- a.result.pkgInfo(obj.Pkg().Path()).addType(v)
- }
-}
-
-// -- utilities --------------------------------------------------------
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if p, ok := typ.Underlying().(*types.Pointer); ok {
- return p.Elem()
- }
- return typ
-}
-
-// isPackageLevel reports whether obj is a package-level object.
-func isPackageLevel(obj types.Object) bool {
- return obj.Pkg().Scope().Lookup(obj.Name()) == obj
-}
diff --git a/oracle/callees14.go b/oracle/callees14.go
deleted file mode 100644
index b6d0ffeb85..0000000000
--- a/oracle/callees14.go
+++ /dev/null
@@ -1,260 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "sort"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-// Callees reports the possible callees of the function call site
-// identified by the specified source location.
-func callees(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); 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) // needs exact pos
- if err != nil {
- return err
- }
-
- // Determine the enclosing call for the specified position.
- var e *ast.CallExpr
- for _, n := range qpos.path {
- if e, _ = n.(*ast.CallExpr); e != nil {
- break
- }
- }
- if e == nil {
- return fmt.Errorf("there is no function call here")
- }
- // TODO(adonovan): issue an error if the call is "too far
- // away" from the current selection, as this most likely is
- // not what the user intended.
-
- // Reject type conversions.
- if qpos.info.Types[e.Fun].IsType() {
- return fmt.Errorf("this is a type conversion, not a function call")
- }
-
- // Deal with obviously static calls before constructing SSA form.
- // Some static calls may yet require SSA construction,
- // e.g. f := func(){}; f().
- switch funexpr := unparen(e.Fun).(type) {
- case *ast.Ident:
- switch obj := qpos.info.Uses[funexpr].(type) {
- case *types.Builtin:
- // Reject calls to built-ins.
- return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
- case *types.Func:
- // This is a static function call
- q.result = &calleesTypesResult{
- site: e,
- callee: obj,
- }
- return nil
- }
- case *ast.SelectorExpr:
- sel := qpos.info.Selections[funexpr]
- if sel == nil {
- // qualified identifier.
- // May refer to top level function variable
- // or to top level function.
- callee := qpos.info.Uses[funexpr.Sel]
- if obj, ok := callee.(*types.Func); ok {
- q.result = &calleesTypesResult{
- site: e,
- callee: obj,
- }
- return nil
- }
- } else if sel.Kind() == types.MethodVal {
- // Inspect the receiver type of the selected method.
- // If it is concrete, the call is statically dispatched.
- // (Due to implicit field selections, it is not enough to look
- // at sel.Recv(), the type of the actual receiver expression.)
- method := sel.Obj().(*types.Func)
- recvtype := method.Type().(*types.Signature).Recv().Type()
- if !types.IsInterface(recvtype) {
- // static method call
- q.result = &calleesTypesResult{
- site: e,
- callee: method,
- }
- return nil
- }
- }
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- pkg := prog.Package(qpos.info.Pkg)
- if pkg == nil {
- return fmt.Errorf("no SSA package")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- // Ascertain calling function and call site.
- callerFn := ssa.EnclosingFunction(pkg, qpos.path)
- if callerFn == nil {
- return fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- // Find the call site.
- site, err := findCallSite(callerFn, e)
- if err != nil {
- return err
- }
-
- funcs, err := findCallees(ptaConfig, site)
- if err != nil {
- return err
- }
-
- q.result = &calleesSSAResult{
- site: site,
- funcs: funcs,
- }
- return nil
-}
-
-func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
- instr, _ := fn.ValueForExpr(call)
- callInstr, _ := instr.(ssa.CallInstruction)
- if instr == nil {
- return nil, fmt.Errorf("this call site is unreachable in this analysis")
- }
- return callInstr, nil
-}
-
-func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) {
- // Avoid running the pointer analysis for static calls.
- if callee := site.Common().StaticCallee(); callee != nil {
- switch callee.String() {
- case "runtime.SetFinalizer", "(reflect.Value).Call":
- // The PTA treats calls to these intrinsics as dynamic.
- // TODO(adonovan): avoid reliance on PTA internals.
-
- default:
- return []*ssa.Function{callee}, nil // singleton
- }
- }
-
- // Dynamic call: use pointer analysis.
- conf.BuildCallGraph = true
- cg := ptrAnalysis(conf).CallGraph
- cg.DeleteSyntheticNodes()
-
- // Find all call edges from the site.
- n := cg.Nodes[site.Parent()]
- if n == nil {
- return nil, fmt.Errorf("this call site is unreachable in this analysis")
- }
- calleesMap := make(map[*ssa.Function]bool)
- for _, edge := range n.Out {
- if edge.Site == site {
- calleesMap[edge.Callee.Func] = true
- }
- }
-
- // De-duplicate and sort.
- funcs := make([]*ssa.Function, 0, len(calleesMap))
- for f := range calleesMap {
- funcs = append(funcs, f)
- }
- sort.Sort(byFuncPos(funcs))
- return funcs, nil
-}
-
-type calleesSSAResult struct {
- site ssa.CallInstruction
- funcs []*ssa.Function
-}
-
-type calleesTypesResult struct {
- site *ast.CallExpr
- callee *types.Func
-}
-
-func (r *calleesSSAResult) display(printf printfFunc) {
- if len(r.funcs) == 0 {
- // dynamic call on a provably nil func/interface
- printf(r.site, "%s on nil value", r.site.Common().Description())
- } else {
- printf(r.site, "this %s dispatches to:", r.site.Common().Description())
- for _, callee := range r.funcs {
- printf(callee, "\t%s", callee)
- }
- }
-}
-
-func (r *calleesSSAResult) toSerial(res *serial.Result, fset *token.FileSet) {
- j := &serial.Callees{
- Pos: fset.Position(r.site.Pos()).String(),
- Desc: r.site.Common().Description(),
- }
- for _, callee := range r.funcs {
- j.Callees = append(j.Callees, &serial.CalleesItem{
- Name: callee.String(),
- Pos: fset.Position(callee.Pos()).String(),
- })
- }
- res.Callees = j
-}
-
-func (r *calleesTypesResult) display(printf printfFunc) {
- printf(r.site, "this static function call dispatches to:")
- printf(r.callee, "\t%s", r.callee.FullName())
-}
-
-func (r *calleesTypesResult) toSerial(res *serial.Result, fset *token.FileSet) {
- j := &serial.Callees{
- Pos: fset.Position(r.site.Pos()).String(),
- Desc: "static function call",
- }
- j.Callees = []*serial.CalleesItem{
- &serial.CalleesItem{
- Name: r.callee.FullName(),
- Pos: fset.Position(r.callee.Pos()).String(),
- },
- }
- res.Callees = j
-}
-
-// NB: byFuncPos is not deterministic across packages since it depends on load order.
-// Use lessPos if the tests need it.
-type byFuncPos []*ssa.Function
-
-func (a byFuncPos) Len() int { return len(a) }
-func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
-func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/oracle/definition14.go b/oracle/definition14.go
deleted file mode 100644
index c22b1fd09b..0000000000
--- a/oracle/definition14.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-// definition reports the location of the definition of an identifier.
-//
-// TODO(adonovan): opt: for intra-file references, the parser's
-// resolution might be enough; we should start with that.
-//
-func definition(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, false)
- if err != nil {
- return err
- }
-
- id, _ := qpos.path[0].(*ast.Ident)
- if id == nil {
- return fmt.Errorf("no identifier here")
- }
-
- obj := qpos.info.ObjectOf(id)
- if obj == nil {
- // Happens for y in "switch y := x.(type)",
- // and the package declaration,
- // but I think that's all.
- return fmt.Errorf("no object for identifier")
- }
-
- q.result = &definitionResult{qpos, obj}
- return nil
-}
-
-type definitionResult struct {
- qpos *queryPos
- obj types.Object // object it denotes
-}
-
-func (r *definitionResult) display(printf printfFunc) {
- printf(r.obj, "defined here as %s", r.qpos.objectString(r.obj))
-}
-
-func (r *definitionResult) toSerial(res *serial.Result, fset *token.FileSet) {
- definition := &serial.Definition{
- Desc: r.obj.String(),
- }
- if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
- definition.ObjPos = fset.Position(pos).String()
- }
- res.Definition = definition
-}
diff --git a/oracle/describe14.go b/oracle/describe14.go
deleted file mode 100644
index c8ccc2d844..0000000000
--- a/oracle/describe14.go
+++ /dev/null
@@ -1,786 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/token"
- "log"
- "os"
- "strings"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
- "golang.org/x/tools/oracle/serial"
-)
-
-// 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"
- case *types.Builtin:
- return "builtin" // e.g. when describing package "unsafe"
- case *types.Nil:
- return "nil"
- case *types.Label:
- return "label"
- }
- 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 *14.go files.
-func constValString(v exact.Value) string {
- if v.Kind() == exact.Float {
- f, _ := exact.Float64Val(v)
- return fmt.Sprintf("%g", f)
- }
- return v.String()
-}
diff --git a/oracle/freevars14.go b/oracle/freevars14.go
deleted file mode 100644
index 760a9e6cfb..0000000000
--- a/oracle/freevars14.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "bytes"
- "go/ast"
- "go/printer"
- "go/token"
- "sort"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-// freevars displays the lexical (not package-level) free variables of
-// the selection.
-//
-// It treats A.B.C as a separate variable from A to reveal the parts
-// of an aggregate type that are actually needed.
-// This aids refactoring.
-//
-// TODO(adonovan): optionally display the free references to
-// file/package scope objects, and to objects from other packages.
-// Depending on where the resulting function abstraction will go,
-// these might be interesting. Perhaps group the results into three
-// bands.
-//
-func freevars(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, false)
- if err != nil {
- return err
- }
-
- file := qpos.path[len(qpos.path)-1] // the enclosing file
- fileScope := qpos.info.Scopes[file]
- pkgScope := fileScope.Parent()
-
- // The id and sel functions return non-nil if they denote an
- // object o or selection o.x.y that is referenced by the
- // selection but defined neither within the selection nor at
- // file scope, i.e. it is in the lexical environment.
- var id func(n *ast.Ident) types.Object
- var sel func(n *ast.SelectorExpr) types.Object
-
- sel = func(n *ast.SelectorExpr) types.Object {
- switch x := unparen(n.X).(type) {
- case *ast.SelectorExpr:
- return sel(x)
- case *ast.Ident:
- return id(x)
- }
- return nil
- }
-
- id = func(n *ast.Ident) types.Object {
- obj := qpos.info.Uses[n]
- if obj == nil {
- return nil // not a reference
- }
- if _, ok := obj.(*types.PkgName); ok {
- return nil // imported package
- }
- if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
- return nil // not defined in this file
- }
- scope := obj.Parent()
- if scope == nil {
- return nil // e.g. interface method, struct field
- }
- if scope == fileScope || scope == pkgScope {
- return nil // defined at file or package scope
- }
- if qpos.start <= obj.Pos() && obj.Pos() <= qpos.end {
- return nil // defined within selection => not free
- }
- return obj
- }
-
- // Maps each reference that is free in the selection
- // to the object it refers to.
- // The map de-duplicates repeated references.
- refsMap := make(map[string]freevarsRef)
-
- // Visit all the identifiers in the selected ASTs.
- ast.Inspect(qpos.path[0], func(n ast.Node) bool {
- if n == nil {
- return true // popping DFS stack
- }
-
- // Is this node contained within the selection?
- // (freevars permits inexact selections,
- // like two stmts in a block.)
- if qpos.start <= n.Pos() && n.End() <= qpos.end {
- var obj types.Object
- var prune bool
- switch n := n.(type) {
- case *ast.Ident:
- obj = id(n)
-
- case *ast.SelectorExpr:
- obj = sel(n)
- prune = true
- }
-
- if obj != nil {
- var kind string
- switch obj.(type) {
- case *types.Var:
- kind = "var"
- case *types.Func:
- kind = "func"
- case *types.TypeName:
- kind = "type"
- case *types.Const:
- kind = "const"
- case *types.Label:
- kind = "label"
- default:
- panic(obj)
- }
-
- typ := qpos.info.TypeOf(n.(ast.Expr))
- ref := freevarsRef{kind, printNode(lprog.Fset, n), typ, obj}
- refsMap[ref.ref] = ref
-
- if prune {
- return false // don't descend
- }
- }
- }
-
- return true // descend
- })
-
- refs := make([]freevarsRef, 0, len(refsMap))
- for _, ref := range refsMap {
- refs = append(refs, ref)
- }
- sort.Sort(byRef(refs))
-
- q.result = &freevarsResult{
- qpos: qpos,
- refs: refs,
- }
- return nil
-}
-
-type freevarsResult struct {
- qpos *queryPos
- refs []freevarsRef
-}
-
-type freevarsRef struct {
- kind string
- ref string
- typ types.Type
- obj types.Object
-}
-
-func (r *freevarsResult) display(printf printfFunc) {
- if len(r.refs) == 0 {
- printf(r.qpos, "No free identifiers.")
- } else {
- printf(r.qpos, "Free identifiers:")
- qualifier := types.RelativeTo(r.qpos.info.Pkg)
- for _, ref := range r.refs {
- // Avoid printing "type T T".
- var typstr string
- if ref.kind != "type" {
- typstr = " " + types.TypeString(ref.typ, qualifier)
- }
- printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr)
- }
- }
-}
-
-func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) {
- var refs []*serial.FreeVar
- for _, ref := range r.refs {
- refs = append(refs,
- &serial.FreeVar{
- Pos: fset.Position(ref.obj.Pos()).String(),
- Kind: ref.kind,
- Ref: ref.ref,
- Type: ref.typ.String(),
- })
- }
- res.Freevars = refs
-}
-
-// -------- utils --------
-
-type byRef []freevarsRef
-
-func (p byRef) Len() int { return len(p) }
-func (p byRef) Less(i, j int) bool { return p[i].ref < p[j].ref }
-func (p byRef) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-
-// printNode returns the pretty-printed syntax of n.
-func printNode(fset *token.FileSet, n ast.Node) string {
- var buf bytes.Buffer
- printer.Fprint(&buf, fset, n)
- return buf.String()
-}
diff --git a/oracle/implements14.go b/oracle/implements14.go
deleted file mode 100644
index 9f4c370210..0000000000
--- a/oracle/implements14.go
+++ /dev/null
@@ -1,354 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "reflect"
- "sort"
- "strings"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
- "golang.org/x/tools/oracle/serial"
- "golang.org/x/tools/refactor/importgraph"
-)
-
-// Implements displays the "implements" relation as it pertains to the
-// selected type.
-// If the selection is a method, 'implements' displays
-// the corresponding methods of the types that would have been reported
-// by an implements query on the receiver type.
-//
-func implements(q *Query) error {
- lconf := loader.Config{Build: q.Build}
- allowErrors(&lconf)
-
- qpkg, err := importQueryPackage(q.Pos, &lconf)
- if err != nil {
- return err
- }
-
- // Set the packages to search.
- if len(q.Scope) > 0 {
- // Inspect all packages in the analysis scope, if specified.
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
- } else {
- // Otherwise inspect the forward and reverse
- // transitive closure of the selected package.
- // (In theory even this is incomplete.)
- _, rev, _ := importgraph.Build(q.Build)
- for path := range rev.Search(qpkg) {
- lconf.ImportWithTests(path)
- }
-
- // TODO(adonovan): for completeness, we should also
- // type-check and inspect function bodies in all
- // imported packages. This would be expensive, but we
- // could optimize by skipping functions that do not
- // contain type declarations. This would require
- // changing the loader's TypeCheckFuncBodies hook to
- // provide the []*ast.File.
- }
-
- // 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, false)
- if err != nil {
- return err
- }
-
- // Find the selected type.
- path, action := findInterestingNode(qpos.info, qpos.path)
-
- var method *types.Func
- var T types.Type // selected type (receiver if method != nil)
-
- switch action {
- case actionExpr:
- // method?
- if id, ok := path[0].(*ast.Ident); ok {
- if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
- recv := obj.Type().(*types.Signature).Recv()
- if recv == nil {
- return fmt.Errorf("this function is not a method")
- }
- method = obj
- T = recv.Type()
- }
- }
- case actionType:
- T = qpos.info.TypeOf(path[0].(ast.Expr))
- }
- if T == nil {
- return fmt.Errorf("no type or method here")
- }
-
- // Find all named types, even local types (which can have
- // methods via promotion) and the built-in "error".
- var allNamed []types.Type
- for _, info := range lprog.AllPackages {
- for _, obj := range info.Defs {
- if obj, ok := obj.(*types.TypeName); ok {
- allNamed = append(allNamed, obj.Type())
- }
- }
- }
- allNamed = append(allNamed, types.Universe.Lookup("error").Type())
-
- var msets typeutil.MethodSetCache
-
- // Test each named type.
- var to, from, fromPtr []types.Type
- for _, U := range allNamed {
- if isInterface(T) {
- if msets.MethodSet(T).Len() == 0 {
- continue // empty interface
- }
- if isInterface(U) {
- if msets.MethodSet(U).Len() == 0 {
- continue // empty interface
- }
-
- // T interface, U interface
- if !types.Identical(T, U) {
- if types.AssignableTo(U, T) {
- to = append(to, U)
- }
- if types.AssignableTo(T, U) {
- from = append(from, U)
- }
- }
- } else {
- // T interface, U concrete
- if types.AssignableTo(U, T) {
- to = append(to, U)
- } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
- to = append(to, pU)
- }
- }
- } else if isInterface(U) {
- if msets.MethodSet(U).Len() == 0 {
- continue // empty interface
- }
-
- // T concrete, U interface
- if types.AssignableTo(T, U) {
- from = append(from, U)
- } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
- fromPtr = append(fromPtr, U)
- }
- }
- }
-
- var pos interface{} = qpos
- if nt, ok := deref(T).(*types.Named); ok {
- pos = nt.Obj()
- }
-
- // Sort types (arbitrarily) to ensure test determinism.
- sort.Sort(typesByString(to))
- sort.Sort(typesByString(from))
- sort.Sort(typesByString(fromPtr))
-
- var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
- if method != nil {
- for _, t := range to {
- toMethod = append(toMethod,
- types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
- }
- for _, t := range from {
- fromMethod = append(fromMethod,
- types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
- }
- for _, t := range fromPtr {
- fromPtrMethod = append(fromPtrMethod,
- types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
- }
- }
-
- q.result = &implementsResult{
- qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
- }
- return nil
-}
-
-type implementsResult struct {
- qpos *queryPos
-
- t types.Type // queried type (not necessarily named)
- pos interface{} // pos of t (*types.Name or *QueryPos)
- to []types.Type // named or ptr-to-named types assignable to interface T
- from []types.Type // named interfaces assignable from T
- fromPtr []types.Type // named interfaces assignable only from *T
-
- // if a method was queried:
- method *types.Func // queried method
- toMethod []*types.Selection // method of type to[i], if any
- fromMethod []*types.Selection // method of type from[i], if any
- fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
-}
-
-func (r *implementsResult) display(printf printfFunc) {
- relation := "is implemented by"
-
- meth := func(sel *types.Selection) {
- if sel != nil {
- printf(sel.Obj(), "\t%s method (%s).%s",
- relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name())
- }
- }
-
- if isInterface(r.t) {
- if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
- printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
- return
- }
-
- if r.method == nil {
- printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
- } else {
- printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
- }
-
- // Show concrete types (or methods) first; use two passes.
- for i, sub := range r.to {
- if !isInterface(sub) {
- if r.method == nil {
- printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
- relation, typeKind(sub), r.qpos.typeString(sub))
- } else {
- meth(r.toMethod[i])
- }
- }
- }
- for i, sub := range r.to {
- if isInterface(sub) {
- if r.method == nil {
- printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
- relation, typeKind(sub), r.qpos.typeString(sub))
- } else {
- meth(r.toMethod[i])
- }
- }
- }
-
- relation = "implements"
- for i, super := range r.from {
- if r.method == nil {
- printf(super.(*types.Named).Obj(), "\t%s %s",
- relation, r.qpos.typeString(super))
- } else {
- meth(r.fromMethod[i])
- }
- }
- } else {
- relation = "implements"
-
- if r.from != nil {
- if r.method == nil {
- printf(r.pos, "%s type %s",
- typeKind(r.t), r.qpos.typeString(r.t))
- } else {
- printf(r.method, "concrete method %s",
- r.qpos.objectString(r.method))
- }
- for i, super := range r.from {
- if r.method == nil {
- printf(super.(*types.Named).Obj(), "\t%s %s",
- relation, r.qpos.typeString(super))
- } else {
- meth(r.fromMethod[i])
- }
- }
- }
- if r.fromPtr != nil {
- if r.method == nil {
- printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
- } else {
- // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
- printf(r.method, "concrete method %s",
- r.qpos.objectString(r.method))
- }
-
- for i, psuper := range r.fromPtr {
- if r.method == nil {
- printf(psuper.(*types.Named).Obj(), "\t%s %s",
- relation, r.qpos.typeString(psuper))
- } else {
- meth(r.fromPtrMethod[i])
- }
- }
- } else if r.from == nil {
- printf(r.pos, "%s type %s implements only interface{}",
- typeKind(r.t), r.qpos.typeString(r.t))
- }
- }
-}
-
-func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
- res.Implements = &serial.Implements{
- T: makeImplementsType(r.t, fset),
- AssignableTo: makeImplementsTypes(r.to, fset),
- AssignableFrom: makeImplementsTypes(r.from, fset),
- AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
- AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
- AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
- AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
- }
- if r.method != nil {
- res.Implements.Method = &serial.DescribeMethod{
- Name: r.qpos.objectString(r.method),
- Pos: fset.Position(r.method.Pos()).String(),
- }
- }
-}
-
-func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
- var r []serial.ImplementsType
- for _, t := range tt {
- r = append(r, makeImplementsType(t, fset))
- }
- return r
-}
-
-func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
- var pos token.Pos
- if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
- pos = nt.Obj().Pos()
- }
- return serial.ImplementsType{
- Name: T.String(),
- Pos: fset.Position(pos).String(),
- Kind: typeKind(T),
- }
-}
-
-// typeKind returns a string describing the underlying kind of type,
-// e.g. "slice", "array", "struct".
-func typeKind(T types.Type) string {
- s := reflect.TypeOf(T.Underlying()).String()
- return strings.ToLower(strings.TrimPrefix(s, "*types."))
-}
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }
-
-type typesByString []types.Type
-
-func (p typesByString) Len() int { return len(p) }
-func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
-func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/oracle/oracle14.go b/oracle/oracle14.go
deleted file mode 100644
index ed8a166d0b..0000000000
--- a/oracle/oracle14.go
+++ /dev/null
@@ -1,367 +0,0 @@
-// 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 oracle contains the implementation of the oracle tool whose
-// command-line is provided by golang.org/x/tools/cmd/oracle.
-//
-// http://golang.org/s/oracle-design
-// http://golang.org/s/oracle-user-manual
-//
-package oracle // import "golang.org/x/tools/oracle"
-
-// This file defines oracle.Query, the entry point for the oracle tool.
-// The actual executable is defined in cmd/oracle.
-
-// TODO(adonovan): new queries
-// - show all statements that may update the selected lvalue
-// (local, global, field, etc).
-// - show all places where an object of type T is created
-// (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
-
-import (
- "fmt"
- "go/ast"
- "go/build"
- "go/parser"
- "go/token"
- "io"
- "path/filepath"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-type printfFunc func(pos interface{}, format string, args ...interface{})
-
-// queryResult is the interface of each query-specific result type.
-type queryResult interface {
- toSerial(res *serial.Result, fset *token.FileSet)
- display(printf printfFunc)
-}
-
-// A QueryPos represents the position provided as input to a query:
-// a textual extent in the program's source code, the AST node it
-// corresponds to, and the package to which it belongs.
-// Instances are created by parseQueryPos.
-type queryPos struct {
- fset *token.FileSet
- start, end token.Pos // source extent of query
- path []ast.Node // AST path from query node to root of ast.File
- exact bool // 2nd result of PathEnclosingInterval
- info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
-}
-
-// TypeString prints type T relative to the query position.
-func (qpos *queryPos) typeString(T types.Type) string {
- return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
-}
-
-// ObjectString prints object obj relative to the query position.
-func (qpos *queryPos) objectString(obj types.Object) string {
- return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
-}
-
-// SelectionString prints selection sel relative to the query position.
-func (qpos *queryPos) selectionString(sel *types.Selection) string {
- return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg))
-}
-
-// A Query specifies a single oracle query.
-type Query struct {
- Mode string // query mode ("callers", etc)
- Pos string // query position
- Build *build.Context // package loading configuration
-
- // pointer analysis options
- Scope []string // main packages in (*loader.Config).FromArgs syntax
- PTALog io.Writer // (optional) pointer-analysis log file
- Reflection bool // model reflection soundly (currently slow).
-
- // Populated during Run()
- Fset *token.FileSet
- result queryResult
-}
-
-// Serial returns an instance of serial.Result, which implements the
-// {xml,json}.Marshaler interfaces so that query results can be
-// serialized as JSON or XML.
-//
-func (q *Query) Serial() *serial.Result {
- resj := &serial.Result{Mode: q.Mode}
- q.result.toSerial(resj, q.Fset)
- return resj
-}
-
-// WriteTo writes the oracle query result res to out in a compiler diagnostic format.
-func (q *Query) WriteTo(out io.Writer) {
- printf := func(pos interface{}, format string, args ...interface{}) {
- fprintf(out, q.Fset, pos, format, args...)
- }
- q.result.display(printf)
-}
-
-// Run runs an oracle query and populates its Fset and Result.
-func Run(q *Query) error {
- switch q.Mode {
- case "callees":
- return callees(q)
- case "callers":
- return callers(q)
- case "callstack":
- return callstack(q)
- case "peers":
- return peers(q)
- case "pointsto":
- return pointsto(q)
- case "whicherrs":
- return whicherrs(q)
- case "definition":
- return definition(q)
- case "describe":
- return describe(q)
- case "freevars":
- return freevars(q)
- case "implements":
- return implements(q)
- case "referrers":
- return referrers(q)
- case "what":
- return what(q)
- default:
- return fmt.Errorf("invalid mode: %q", q.Mode)
- }
-}
-
-func setPTAScope(lconf *loader.Config, scope []string) error {
- if len(scope) == 0 {
- return fmt.Errorf("no packages specified for pointer analysis scope")
- }
-
- // Determine initial packages for PTA.
- args, err := lconf.FromArgs(scope, true)
- if err != nil {
- return err
- }
- if len(args) > 0 {
- return fmt.Errorf("surplus arguments: %q", args)
- }
- return nil
-}
-
-// Create a pointer.Config whose scope is the initial packages of lprog
-// and their dependencies.
-func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
- // TODO(adonovan): the body of this function is essentially
- // duplicated in all go/pointer clients. Refactor.
-
- // For each initial package (specified on the command line),
- // if it has a main function, analyze that,
- // otherwise analyze its tests, if any.
- var testPkgs, mains []*ssa.Package
- for _, info := range lprog.InitialPackages() {
- initialPkg := prog.Package(info.Pkg)
-
- // Add package to the pointer analysis scope.
- if initialPkg.Func("main") != nil {
- mains = append(mains, initialPkg)
- } else {
- testPkgs = append(testPkgs, initialPkg)
- }
- }
- if testPkgs != nil {
- if p := prog.CreateTestMainPackage(testPkgs...); p != nil {
- mains = append(mains, p)
- }
- }
- if mains == nil {
- return nil, fmt.Errorf("analysis scope has no main and no tests")
- }
- return &pointer.Config{
- Log: ptaLog,
- Reflection: reflection,
- Mains: mains,
- }, nil
-}
-
-// importQueryPackage finds the package P containing the
-// query position and tells conf to import it.
-// It returns the package's path.
-func importQueryPackage(pos string, conf *loader.Config) (string, error) {
- fqpos, err := fastQueryPos(pos)
- if err != nil {
- return "", err // bad query
- }
- filename := fqpos.fset.File(fqpos.start).Name()
-
- // This will not work for ad-hoc packages
- // such as $GOROOT/src/net/http/triv.go.
- // TODO(adonovan): ensure we report a clear error.
- _, importPath, err := guessImportPath(filename, conf.Build)
- if err != nil {
- return "", err // can't find GOPATH dir
- }
- if importPath == "" {
- return "", fmt.Errorf("can't guess import path from %s", filename)
- }
-
- // Check that it's possible to load the queried package.
- // (e.g. oracle tests contain different 'package' decls in same dir.)
- // Keep consistent with logic in loader/util.go!
- cfg2 := *conf.Build
- cfg2.CgoEnabled = false
- bp, err := cfg2.Import(importPath, "", 0)
- if err != nil {
- return "", err // no files for package
- }
-
- switch pkgContainsFile(bp, filename) {
- case 'T':
- conf.ImportWithTests(importPath)
- case 'X':
- conf.ImportWithTests(importPath)
- importPath += "_test" // for TypeCheckFuncBodies
- case 'G':
- conf.Import(importPath)
- default:
- return "", fmt.Errorf("package %q doesn't contain file %s",
- importPath, filename)
- }
-
- conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
-
- return importPath, nil
-}
-
-// pkgContainsFile reports whether file was among the packages Go
-// files, Test files, eXternal test files, or not found.
-func pkgContainsFile(bp *build.Package, filename string) byte {
- for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
- for _, file := range files {
- if sameFile(filepath.Join(bp.Dir, file), filename) {
- return "GTX"[i]
- }
- }
- }
- return 0 // not found
-}
-
-// ParseQueryPos parses the source query position pos and returns the
-// AST node of the loaded program lprog that it identifies.
-// If needExact, it must identify a single AST subtree;
-// this is appropriate for queries that allow fairly arbitrary syntax,
-// e.g. "describe".
-//
-func parseQueryPos(lprog *loader.Program, posFlag string, needExact bool) (*queryPos, error) {
- filename, startOffset, endOffset, err := parsePosFlag(posFlag)
- if err != nil {
- return nil, err
- }
- start, end, err := findQueryPos(lprog.Fset, filename, startOffset, endOffset)
- if err != nil {
- return nil, err
- }
- info, path, exact := lprog.PathEnclosingInterval(start, end)
- if path == nil {
- return nil, fmt.Errorf("no syntax here")
- }
- if needExact && !exact {
- return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
- }
- return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
-}
-
-// ---------- Utilities ----------
-
-// allowErrors causes type errors to be silently ignored.
-// (Not suitable if SSA construction follows.)
-func allowErrors(lconf *loader.Config) {
- ctxt := *lconf.Build // copy
- ctxt.CgoEnabled = false
- lconf.Build = &ctxt
- lconf.AllowErrors = true
- // AllErrors makes the parser always return an AST instead of
- // bailing out after 10 errors and returning an empty ast.File.
- lconf.ParserMode = parser.AllErrors
- lconf.TypeChecker.Error = func(err error) {}
-}
-
-// ptrAnalysis runs the pointer analysis and returns its result.
-func ptrAnalysis(conf *pointer.Config) *pointer.Result {
- result, err := pointer.Analyze(conf)
- if err != nil {
- panic(err) // pointer analysis internal error
- }
- return result
-}
-
-func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if p, ok := typ.Underlying().(*types.Pointer); ok {
- return p.Elem()
- }
- return typ
-}
-
-// fprintf prints to w a message of the form "location: message\n"
-// where location is derived from pos.
-//
-// pos must be one of:
-// - a token.Pos, denoting a position
-// - an ast.Node, denoting an interval
-// - anything with a Pos() method:
-// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
-// - a QueryPos, denoting the extent of the user's query.
-// - nil, meaning no position at all.
-//
-// The output format is is compatible with the 'gnu'
-// compilation-error-regexp in Emacs' compilation mode.
-// TODO(adonovan): support other editors.
-//
-func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
- var start, end token.Pos
- switch pos := pos.(type) {
- case ast.Node:
- start = pos.Pos()
- end = pos.End()
- case token.Pos:
- start = pos
- end = start
- case interface {
- Pos() token.Pos
- }:
- start = pos.Pos()
- end = start
- case *queryPos:
- start = pos.start
- end = pos.end
- case nil:
- // no-op
- default:
- panic(fmt.Sprintf("invalid pos: %T", pos))
- }
-
- if sp := fset.Position(start); start == end {
- // (prints "-: " for token.NoPos)
- fmt.Fprintf(w, "%s: ", sp)
- } else {
- ep := fset.Position(end)
- // The -1 below is a concession to Emacs's broken use of
- // inclusive (not half-open) intervals.
- // Other editors may not want it.
- // TODO(adonovan): add an -editor=vim|emacs|acme|auto
- // flag; auto uses EMACS=t / VIM=... / etc env vars.
- fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
- sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
- }
- fmt.Fprintf(w, format, args...)
- io.WriteString(w, "\n")
-}
diff --git a/oracle/peers14.go b/oracle/peers14.go
deleted file mode 100644
index 9b97cbf2d7..0000000000
--- a/oracle/peers14.go
+++ /dev/null
@@ -1,254 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "sort"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-// peers enumerates, for a given channel send (or receive) operation,
-// the set of possible receives (or sends) that correspond to it.
-//
-// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
-// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
-// or the implicit receive in "for v := range ch".
-func peers(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); 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, false)
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- opPos := findOp(qpos)
- if opPos == token.NoPos {
- return fmt.Errorf("there is no channel operation here")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- var queryOp chanOp // the originating send or receive operation
- var ops []chanOp // all sends/receives of opposite direction
-
- // Look at all channel operations in the whole ssa.Program.
- // Build a list of those of same type as the query.
- allFuncs := ssautil.AllFunctions(prog)
- for fn := range allFuncs {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- for _, op := range chanOps(instr) {
- ops = append(ops, op)
- if op.pos == opPos {
- queryOp = op // we found the query op
- }
- }
- }
- }
- }
- if queryOp.ch == nil {
- return fmt.Errorf("ssa.Instruction for send/receive not found")
- }
-
- // Discard operations of wrong channel element type.
- // Build set of channel ssa.Values as query to pointer analysis.
- // We compare channels by element types, not channel types, to
- // ignore both directionality and type names.
- queryType := queryOp.ch.Type()
- queryElemType := queryType.Underlying().(*types.Chan).Elem()
- ptaConfig.AddQuery(queryOp.ch)
- i := 0
- for _, op := range ops {
- if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
- ptaConfig.AddQuery(op.ch)
- ops[i] = op
- i++
- }
- }
- ops = ops[:i]
-
- // Run the pointer analysis.
- ptares := ptrAnalysis(ptaConfig)
-
- // Find the points-to set.
- queryChanPtr := ptares.Queries[queryOp.ch]
-
- // Ascertain which make(chan) labels the query's channel can alias.
- var makes []token.Pos
- for _, label := range queryChanPtr.PointsTo().Labels() {
- makes = append(makes, label.Pos())
- }
- sort.Sort(byPos(makes))
-
- // Ascertain which channel operations can alias the same make(chan) labels.
- var sends, receives, closes []token.Pos
- for _, op := range ops {
- if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
- switch op.dir {
- case types.SendOnly:
- sends = append(sends, op.pos)
- case types.RecvOnly:
- receives = append(receives, op.pos)
- case types.SendRecv:
- closes = append(closes, op.pos)
- }
- }
- }
- sort.Sort(byPos(sends))
- sort.Sort(byPos(receives))
- sort.Sort(byPos(closes))
-
- q.result = &peersResult{
- queryPos: opPos,
- queryType: queryType,
- makes: makes,
- sends: sends,
- receives: receives,
- closes: closes,
- }
- return nil
-}
-
-// findOp returns the position of the enclosing send/receive/close op.
-// For send and receive operations, this is the position of the <- token;
-// for close operations, it's the Lparen of the function call.
-//
-// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
-func findOp(qpos *queryPos) token.Pos {
- for _, n := range qpos.path {
- switch n := n.(type) {
- case *ast.UnaryExpr:
- if n.Op == token.ARROW {
- return n.OpPos
- }
- case *ast.SendStmt:
- return n.Arrow
- case *ast.CallExpr:
- // close function call can only exist as a direct identifier
- if close, ok := unparen(n.Fun).(*ast.Ident); ok {
- if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
- return n.Lparen
- }
- }
- }
- }
- return token.NoPos
-}
-
-// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
-type chanOp struct {
- ch ssa.Value
- dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
- pos token.Pos
-}
-
-// chanOps returns a slice of all the channel operations in the instruction.
-func chanOps(instr ssa.Instruction) []chanOp {
- // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
- var ops []chanOp
- switch instr := instr.(type) {
- case *ssa.UnOp:
- if instr.Op == token.ARROW {
- ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
- }
- case *ssa.Send:
- ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
- case *ssa.Select:
- for _, st := range instr.States {
- ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
- }
- case ssa.CallInstruction:
- cc := instr.Common()
- if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
- ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
- }
- }
- return ops
-}
-
-type peersResult struct {
- queryPos token.Pos // of queried channel op
- queryType types.Type // type of queried channel
- makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
-}
-
-func (r *peersResult) display(printf printfFunc) {
- if len(r.makes) == 0 {
- printf(r.queryPos, "This channel can't point to anything.")
- return
- }
- printf(r.queryPos, "This channel of type %s may be:", r.queryType)
- for _, alloc := range r.makes {
- printf(alloc, "\tallocated here")
- }
- for _, send := range r.sends {
- printf(send, "\tsent to, here")
- }
- for _, receive := range r.receives {
- printf(receive, "\treceived from, here")
- }
- for _, clos := range r.closes {
- printf(clos, "\tclosed, here")
- }
-}
-
-func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
- peers := &serial.Peers{
- Pos: fset.Position(r.queryPos).String(),
- Type: r.queryType.String(),
- }
- for _, alloc := range r.makes {
- peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
- }
- for _, send := range r.sends {
- peers.Sends = append(peers.Sends, fset.Position(send).String())
- }
- for _, receive := range r.receives {
- peers.Receives = append(peers.Receives, fset.Position(receive).String())
- }
- for _, clos := range r.closes {
- peers.Closes = append(peers.Closes, fset.Position(clos).String())
- }
- res.Peers = peers
-}
-
-// -------- utils --------
-
-// NB: byPos is not deterministic across packages since it depends on load order.
-// Use lessPos if the tests need it.
-type byPos []token.Pos
-
-func (p byPos) Len() int { return len(p) }
-func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
-func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/oracle/pointsto14.go b/oracle/pointsto14.go
deleted file mode 100644
index 1e406199e8..0000000000
--- a/oracle/pointsto14.go
+++ /dev/null
@@ -1,293 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "sort"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-// pointsto runs the pointer analysis on the selected expression,
-// and reports its points-to set (for a pointer-like expression)
-// or its dynamic types (for an interface, reflect.Value, or
-// reflect.Type expression) and their points-to sets.
-//
-// All printed sets are sorted to ensure determinism.
-//
-func pointsto(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); 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) // needs exact pos
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- path, action := findInterestingNode(qpos.info, qpos.path)
- if action != actionExpr {
- return fmt.Errorf("pointer analysis wants an expression; got %s",
- astutil.NodeDescription(qpos.path[0]))
- }
-
- var expr ast.Expr
- var obj types.Object
- switch n := path[0].(type) {
- case *ast.ValueSpec:
- // ambiguous ValueSpec containing multiple names
- return 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 fmt.Errorf("unexpected AST for expr: %T", n)
- }
-
- // Reject non-pointerlike types (includes all constants---except nil).
- // TODO(adonovan): reject nil too.
- typ := qpos.info.TypeOf(expr)
- if !pointer.CanPoint(typ) {
- return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ)
- }
-
- // Determine the ssa.Value for the expression.
- var value ssa.Value
- var isAddr bool
- if obj != nil {
- // def/ref of func/var object
- value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path)
- } else {
- value, isAddr, err = ssaValueForExpr(prog, qpos.info, path)
- }
- if err != nil {
- return err // e.g. trivially dead code
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- // Run the pointer analysis.
- ptrs, err := runPTA(ptaConfig, value, isAddr)
- if err != nil {
- return err // e.g. analytically unreachable
- }
-
- q.result = &pointstoResult{
- qpos: qpos,
- typ: typ,
- ptrs: ptrs,
- }
- return nil
-}
-
-// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
-// to the root of the AST is path. isAddr reports whether the
-// ssa.Value is the address denoted by the ast.Ident, not its value.
-//
-func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
- switch obj := obj.(type) {
- case *types.Var:
- pkg := prog.Package(qinfo.Pkg)
- pkg.Build()
- if v, addr := prog.VarValue(obj, pkg, path); v != nil {
- return v, addr, nil
- }
- return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
-
- case *types.Func:
- fn := prog.FuncValue(obj)
- if fn == nil {
- return nil, false, fmt.Errorf("%s is an interface method", obj)
- }
- // TODO(adonovan): there's no point running PTA on a *Func ident.
- // Eliminate this feature.
- return fn, false, nil
- }
- panic(obj)
-}
-
-// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
-// expression whose path to the root of the AST is path.
-//
-func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
- pkg := prog.Package(qinfo.Pkg)
- pkg.SetDebugMode(true)
- pkg.Build()
-
- fn := ssa.EnclosingFunction(pkg, path)
- if fn == nil {
- return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
- return v, addr, nil
- }
-
- return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
-}
-
-// runPTA runs the pointer analysis of the selected SSA value or address.
-func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
- T := v.Type()
- if isAddr {
- conf.AddIndirectQuery(v)
- T = deref(T)
- } else {
- conf.AddQuery(v)
- }
- ptares := ptrAnalysis(conf)
-
- var ptr pointer.Pointer
- if isAddr {
- ptr = ptares.IndirectQueries[v]
- } else {
- ptr = ptares.Queries[v]
- }
- if ptr == (pointer.Pointer{}) {
- return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
- }
- pts := ptr.PointsTo()
-
- if pointer.CanHaveDynamicTypes(T) {
- // Show concrete types for interface/reflect.Value expression.
- if concs := pts.DynamicTypes(); concs.Len() > 0 {
- concs.Iterate(func(conc types.Type, pta interface{}) {
- labels := pta.(pointer.PointsToSet).Labels()
- sort.Sort(byPosAndString(labels)) // to ensure determinism
- ptrs = append(ptrs, pointerResult{conc, labels})
- })
- }
- } else {
- // Show labels for other expressions.
- labels := pts.Labels()
- sort.Sort(byPosAndString(labels)) // to ensure determinism
- ptrs = append(ptrs, pointerResult{T, labels})
- }
- sort.Sort(byTypeString(ptrs)) // to ensure determinism
- return ptrs, nil
-}
-
-type pointerResult struct {
- typ types.Type // type of the pointer (always concrete)
- labels []*pointer.Label // set of labels
-}
-
-type pointstoResult struct {
- qpos *queryPos
- typ types.Type // type of expression
- ptrs []pointerResult // pointer info (typ is concrete => len==1)
-}
-
-func (r *pointstoResult) display(printf printfFunc) {
- if pointer.CanHaveDynamicTypes(r.typ) {
- // Show concrete types for interface, reflect.Type or
- // reflect.Value expression.
-
- if len(r.ptrs) > 0 {
- printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ))
- for _, ptr := range r.ptrs {
- var obj types.Object
- if nt, ok := deref(ptr.typ).(*types.Named); ok {
- obj = nt.Obj()
- }
- if len(ptr.labels) > 0 {
- printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ))
- printLabels(printf, ptr.labels, "\t\t")
- } else {
- printf(obj, "\t%s", r.qpos.typeString(ptr.typ))
- }
- }
- } else {
- printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
- }
- } else {
- // Show labels for other expressions.
- if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
- printf(r.qpos, "this %s may point to these objects:",
- r.qpos.typeString(r.typ))
- printLabels(printf, ptr.labels, "\t")
- } else {
- printf(r.qpos, "this %s may not point to anything.",
- r.qpos.typeString(r.typ))
- }
- }
-}
-
-func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) {
- var pts []serial.PointsTo
- for _, ptr := range r.ptrs {
- var namePos string
- if nt, ok := deref(ptr.typ).(*types.Named); ok {
- namePos = fset.Position(nt.Obj().Pos()).String()
- }
- var labels []serial.PointsToLabel
- for _, l := range ptr.labels {
- labels = append(labels, serial.PointsToLabel{
- Pos: fset.Position(l.Pos()).String(),
- Desc: l.String(),
- })
- }
- pts = append(pts, serial.PointsTo{
- Type: r.qpos.typeString(ptr.typ),
- NamePos: namePos,
- Labels: labels,
- })
- }
- res.PointsTo = pts
-}
-
-type byTypeString []pointerResult
-
-func (a byTypeString) Len() int { return len(a) }
-func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
-func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type byPosAndString []*pointer.Label
-
-func (a byPosAndString) Len() int { return len(a) }
-func (a byPosAndString) Less(i, j int) bool {
- cmp := a[i].Pos() - a[j].Pos()
- return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
-}
-func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
- // TODO(adonovan): due to context-sensitivity, many of these
- // labels may differ only by context, which isn't apparent.
- for _, label := range labels {
- printf(label, "%s%s", prefix, label)
- }
-}
diff --git a/oracle/referrers14.go b/oracle/referrers14.go
deleted file mode 100644
index 17adf023d5..0000000000
--- a/oracle/referrers14.go
+++ /dev/null
@@ -1,243 +0,0 @@
-// 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
-
-package oracle
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/token"
- "io/ioutil"
- "sort"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
- "golang.org/x/tools/refactor/importgraph"
-)
-
-// Referrers reports all identifiers that resolve to the same object
-// as the queried identifier, within any package in the analysis scope.
-func referrers(q *Query) error {
- lconf := loader.Config{Build: q.Build}
- allowErrors(&lconf)
-
- if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
- return err
- }
-
- var id *ast.Ident
- var obj types.Object
- var lprog *loader.Program
- var pass2 bool
- var qpos *queryPos
- for {
- // Load/parse/type-check the program.
- var err error
- lprog, err = lconf.Load()
- if err != nil {
- return err
- }
- q.Fset = lprog.Fset
-
- qpos, err = parseQueryPos(lprog, q.Pos, false)
- if err != nil {
- return err
- }
-
- id, _ = qpos.path[0].(*ast.Ident)
- if id == nil {
- return fmt.Errorf("no identifier here")
- }
-
- obj = qpos.info.ObjectOf(id)
- if obj == nil {
- // Happens for y in "switch y := x.(type)",
- // the package declaration,
- // and unresolved identifiers.
- if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
- pkg := qpos.info.Pkg
- obj = types.NewPkgName(id.Pos(), pkg, pkg.Name(), pkg)
- } else {
- return fmt.Errorf("no object for identifier: %T", qpos.path[1])
- }
- }
-
- if pass2 {
- break
- }
-
- // If the identifier is exported, we must load all packages that
- // depend transitively upon the package that defines it.
- // Treat PkgNames as exported, even though they're lowercase.
- if _, isPkg := obj.(*types.PkgName); !(isPkg || obj.Exported()) {
- break // not exported
- }
-
- // Scan the workspace and build the import graph.
- // Ignore broken packages.
- _, rev, _ := importgraph.Build(q.Build)
-
- // Re-load the larger program.
- // Create a new file set so that ...
- // External test packages are never imported,
- // so they will never appear in the graph.
- // (We must reset the Config here, not just reset the Fset field.)
- lconf = loader.Config{
- Fset: token.NewFileSet(),
- Build: q.Build,
- }
- allowErrors(&lconf)
- for path := range rev.Search(obj.Pkg().Path()) {
- lconf.ImportWithTests(path)
- }
- pass2 = true
- }
-
- // Iterate over all go/types' Uses facts for the entire program.
- var refs []*ast.Ident
- for _, info := range lprog.AllPackages {
- for id2, obj2 := range info.Uses {
- if sameObj(obj, obj2) {
- refs = append(refs, id2)
- }
- }
- }
- sort.Sort(byNamePos{q.Fset, refs})
-
- q.result = &referrersResult{
- qpos: qpos,
- query: id,
- obj: obj,
- refs: refs,
- }
- return nil
-}
-
-// same reports whether x and y are identical, or both are PkgNames
-// that import the same Package.
-//
-func sameObj(x, y types.Object) bool {
- if x == y {
- return true
- }
- if x, ok := x.(*types.PkgName); ok {
- if y, ok := y.(*types.PkgName); ok {
- return x.Imported() == y.Imported()
- }
- }
- return false
-}
-
-// -------- utils --------
-
-// An deterministic ordering for token.Pos that doesn't
-// depend on the order in which packages were loaded.
-func lessPos(fset *token.FileSet, x, y token.Pos) bool {
- fx := fset.File(x)
- fy := fset.File(y)
- if fx != fy {
- return fx.Name() < fy.Name()
- }
- return x < y
-}
-
-type byNamePos struct {
- fset *token.FileSet
- ids []*ast.Ident
-}
-
-func (p byNamePos) Len() int { return len(p.ids) }
-func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
-func (p byNamePos) Less(i, j int) bool {
- return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
-}
-
-type referrersResult struct {
- qpos *queryPos
- query *ast.Ident // identifier of query
- obj types.Object // object it denotes
- refs []*ast.Ident // set of all other references to it
-}
-
-func (r *referrersResult) display(printf printfFunc) {
- printf(r.obj, "%d references to %s", len(r.refs), r.qpos.objectString(r.obj))
-
- // Show referring lines, like grep.
- type fileinfo struct {
- refs []*ast.Ident
- linenums []int // line number of refs[i]
- data chan interface{} // file contents or error
- }
- var fileinfos []*fileinfo
- fileinfosByName := make(map[string]*fileinfo)
-
- // First pass: start the file reads concurrently.
- sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
- for _, ref := range r.refs {
- posn := r.qpos.fset.Position(ref.Pos())
- fi := fileinfosByName[posn.Filename]
- if fi == nil {
- fi = &fileinfo{data: make(chan interface{})}
- fileinfosByName[posn.Filename] = fi
- fileinfos = append(fileinfos, fi)
-
- // First request for this file:
- // start asynchronous read.
- go func() {
- sema <- struct{}{} // acquire token
- content, err := ioutil.ReadFile(posn.Filename)
- <-sema // release token
- if err != nil {
- fi.data <- err
- } else {
- fi.data <- content
- }
- }()
- }
- fi.refs = append(fi.refs, ref)
- fi.linenums = append(fi.linenums, posn.Line)
- }
-
- // Second pass: print refs in original order.
- // One line may have several refs at different columns.
- for _, fi := range fileinfos {
- v := <-fi.data // wait for I/O completion
-
- // Print one item for all refs in a file that could not
- // be loaded (perhaps due to //line directives).
- if err, ok := v.(error); ok {
- var suffix string
- if more := len(fi.refs) - 1; more > 0 {
- suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
- }
- printf(fi.refs[0], "%v%s", err, suffix)
- continue
- }
-
- lines := bytes.Split(v.([]byte), []byte("\n"))
- for i, ref := range fi.refs {
- printf(ref, "%s", lines[fi.linenums[i]-1])
- }
- }
-}
-
-// TODO(adonovan): encode extent, not just Pos info, in Serial form.
-
-func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) {
- referrers := &serial.Referrers{
- Pos: fset.Position(r.query.Pos()).String(),
- Desc: r.obj.String(),
- }
- if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
- referrers.ObjPos = fset.Position(pos).String()
- }
- for _, ref := range r.refs {
- referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String())
- }
- res.Referrers = referrers
-}
diff --git a/oracle/whicherrs14.go b/oracle/whicherrs14.go
deleted file mode 100644
index 25449f3183..0000000000
--- a/oracle/whicherrs14.go
+++ /dev/null
@@ -1,328 +0,0 @@
-// 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 oracle
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "sort"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/oracle/serial"
-)
-
-var builtinErrorType = types.Universe.Lookup("error").Type()
-
-// whicherrs takes an position to an error and tries to find all types, constants
-// and global value which a given error can point to and which can be checked from the
-// scope where the error lives.
-// In short, it returns a list of things that can be checked against in order to handle
-// an error properly.
-//
-// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
-// can be queried recursively somehow.
-func whicherrs(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); 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) // needs exact pos
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- path, action := findInterestingNode(qpos.info, qpos.path)
- if action != actionExpr {
- return fmt.Errorf("whicherrs wants an expression; got %s",
- astutil.NodeDescription(qpos.path[0]))
- }
- var expr ast.Expr
- var obj types.Object
- switch n := path[0].(type) {
- case *ast.ValueSpec:
- // ambiguous ValueSpec containing multiple names
- return fmt.Errorf("multiple value specification")
- case *ast.Ident:
- obj = qpos.info.ObjectOf(n)
- expr = n
- case ast.Expr:
- expr = n
- default:
- return fmt.Errorf("unexpected AST for expr: %T", n)
- }
-
- typ := qpos.info.TypeOf(expr)
- if !types.Identical(typ, builtinErrorType) {
- return fmt.Errorf("selection is not an expression of type 'error'")
- }
- // Determine the ssa.Value for the expression.
- var value ssa.Value
- if obj != nil {
- // def/ref of func/var object
- value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
- } else {
- value, _, err = ssaValueForExpr(prog, qpos.info, path)
- }
- if err != nil {
- return err // e.g. trivially dead code
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- globals := findVisibleErrs(prog, qpos)
- constants := findVisibleConsts(prog, qpos)
-
- res := &whicherrsResult{
- qpos: qpos,
- errpos: expr.Pos(),
- }
-
- // TODO(adonovan): the following code is heavily duplicated
- // w.r.t. "pointsto". Refactor?
-
- // Find the instruction which initialized the
- // global error. If more than one instruction has stored to the global
- // remove the global from the set of values that we want to query.
- allFuncs := ssautil.AllFunctions(prog)
- for fn := range allFuncs {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- store, ok := instr.(*ssa.Store)
- if !ok {
- continue
- }
- gval, ok := store.Addr.(*ssa.Global)
- if !ok {
- continue
- }
- gbl, ok := globals[gval]
- if !ok {
- continue
- }
- // we already found a store to this global
- // The normal error define is just one store in the init
- // so we just remove this global from the set we want to query
- if gbl != nil {
- delete(globals, gval)
- }
- globals[gval] = store.Val
- }
- }
- }
-
- ptaConfig.AddQuery(value)
- for _, v := range globals {
- ptaConfig.AddQuery(v)
- }
-
- ptares := ptrAnalysis(ptaConfig)
- valueptr := ptares.Queries[value]
- for g, v := range globals {
- ptr, ok := ptares.Queries[v]
- if !ok {
- continue
- }
- if !ptr.MayAlias(valueptr) {
- continue
- }
- res.globals = append(res.globals, g)
- }
- pts := valueptr.PointsTo()
- dedup := make(map[*ssa.NamedConst]bool)
- for _, label := range pts.Labels() {
- // These values are either MakeInterfaces or reflect
- // generated interfaces. For the purposes of this
- // analysis, we don't care about reflect generated ones
- makeiface, ok := label.Value().(*ssa.MakeInterface)
- if !ok {
- continue
- }
- constval, ok := makeiface.X.(*ssa.Const)
- if !ok {
- continue
- }
- c := constants[*constval]
- if c != nil && !dedup[c] {
- dedup[c] = true
- res.consts = append(res.consts, c)
- }
- }
- concs := pts.DynamicTypes()
- concs.Iterate(func(conc types.Type, _ interface{}) {
- // go/types is a bit annoying here.
- // We want to find all the types that we can
- // typeswitch or assert to. This means finding out
- // if the type pointed to can be seen by us.
- //
- // For the purposes of this analysis, the type is always
- // either a Named type or a pointer to one.
- // There are cases where error can be implemented
- // by unnamed types, but in that case, we can't assert to
- // it, so we don't care about it for this analysis.
- var name *types.TypeName
- switch t := conc.(type) {
- case *types.Pointer:
- named, ok := t.Elem().(*types.Named)
- if !ok {
- return
- }
- name = named.Obj()
- case *types.Named:
- name = t.Obj()
- default:
- return
- }
- if !isAccessibleFrom(name, qpos.info.Pkg) {
- return
- }
- res.types = append(res.types, &errorType{conc, name})
- })
- sort.Sort(membersByPosAndString(res.globals))
- sort.Sort(membersByPosAndString(res.consts))
- sort.Sort(sorterrorType(res.types))
-
- q.result = res
- return nil
-}
-
-// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
-func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
- globals := make(map[*ssa.Global]ssa.Value)
- for _, pkg := range prog.AllPackages() {
- for _, mem := range pkg.Members {
- gbl, ok := mem.(*ssa.Global)
- if !ok {
- continue
- }
- gbltype := gbl.Type()
- // globals are always pointers
- if !types.Identical(deref(gbltype), builtinErrorType) {
- continue
- }
- if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
- continue
- }
- globals[gbl] = nil
- }
- }
- return globals
-}
-
-// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
-func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
- constants := make(map[ssa.Const]*ssa.NamedConst)
- for _, pkg := range prog.AllPackages() {
- for _, mem := range pkg.Members {
- obj, ok := mem.(*ssa.NamedConst)
- if !ok {
- continue
- }
- consttype := obj.Type()
- if !types.AssignableTo(consttype, builtinErrorType) {
- continue
- }
- if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
- continue
- }
- constants[*obj.Value] = obj
- }
- }
-
- return constants
-}
-
-type membersByPosAndString []ssa.Member
-
-func (a membersByPosAndString) Len() int { return len(a) }
-func (a membersByPosAndString) Less(i, j int) bool {
- cmp := a[i].Pos() - a[j].Pos()
- return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
-}
-func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type sorterrorType []*errorType
-
-func (a sorterrorType) Len() int { return len(a) }
-func (a sorterrorType) Less(i, j int) bool {
- cmp := a[i].obj.Pos() - a[j].obj.Pos()
- return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
-}
-func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type errorType struct {
- typ types.Type // concrete type N or *N that implements error
- obj *types.TypeName // the named type N
-}
-
-type whicherrsResult struct {
- qpos *queryPos
- errpos token.Pos
- globals []ssa.Member
- consts []ssa.Member
- types []*errorType
-}
-
-func (r *whicherrsResult) display(printf printfFunc) {
- if len(r.globals) > 0 {
- printf(r.qpos, "this error may point to these globals:")
- for _, g := range r.globals {
- printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
- }
- }
- if len(r.consts) > 0 {
- printf(r.qpos, "this error may contain these constants:")
- for _, c := range r.consts {
- printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
- }
- }
- if len(r.types) > 0 {
- printf(r.qpos, "this error may contain these dynamic types:")
- for _, t := range r.types {
- printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
- }
- }
-}
-
-func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) {
- we := &serial.WhichErrs{}
- we.ErrPos = fset.Position(r.errpos).String()
- for _, g := range r.globals {
- we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
- }
- for _, c := range r.consts {
- we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
- }
- for _, t := range r.types {
- var et serial.WhichErrsType
- et.Type = r.qpos.typeString(t.typ)
- et.Position = fset.Position(t.obj.Pos()).String()
- we.Types = append(we.Types, et)
- }
- res.WhichErrs = we
-}
diff --git a/refactor/rename/check14.go b/refactor/rename/check14.go
deleted file mode 100644
index 3f1f7abf22..0000000000
--- a/refactor/rename/check14.go
+++ /dev/null
@@ -1,860 +0,0 @@
-// 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 rename
-
-// This file defines the safety checks for each kind of renaming.
-
-import (
- "fmt"
- "go/ast"
- "go/token"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/refactor/satisfy"
-)
-
-// errorf reports an error (e.g. conflict) and prevents file modification.
-func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) {
- r.hadConflicts = true
- reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...))
-}
-
-// check performs safety checks of the renaming of the 'from' object to r.to.
-func (r *renamer) check(from types.Object) {
- if r.objsToUpdate[from] {
- return
- }
- r.objsToUpdate[from] = true
-
- // NB: order of conditions is important.
- if from_, ok := from.(*types.PkgName); ok {
- r.checkInFileBlock(from_)
- } else if from_, ok := from.(*types.Label); ok {
- r.checkLabel(from_)
- } else if isPackageLevel(from) {
- r.checkInPackageBlock(from)
- } else if v, ok := from.(*types.Var); ok && v.IsField() {
- r.checkStructField(v)
- } else if f, ok := from.(*types.Func); ok && recv(f) != nil {
- r.checkMethod(f)
- } else if isLocal(from) {
- r.checkInLocalScope(from)
- } else {
- r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
- objectKind(from), from)
- }
-}
-
-// checkInFileBlock performs safety checks for renames of objects in the file block,
-// i.e. imported package names.
-func (r *renamer) checkInFileBlock(from *types.PkgName) {
- // Check import name is not "init".
- if r.to == "init" {
- r.errorf(from.Pos(), "%q is not a valid imported package name", r.to)
- }
-
- // Check for conflicts between file and package block.
- if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
- objectKind(from), from.Name(), r.to)
- r.errorf(prev.Pos(), "\twith this package member %s",
- objectKind(prev))
- return // since checkInPackageBlock would report redundant errors
- }
-
- // Check for conflicts in lexical scope.
- r.checkInLexicalScope(from, r.packages[from.Pkg()])
-
- // Finally, modify ImportSpec syntax to add or remove the Name as needed.
- info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
- if from.Imported().Name() == r.to {
- // ImportSpec.Name not needed
- path[1].(*ast.ImportSpec).Name = nil
- } else {
- // ImportSpec.Name needed
- if spec := path[1].(*ast.ImportSpec); spec.Name == nil {
- spec.Name = &ast.Ident{NamePos: spec.Path.Pos(), Name: r.to}
- info.Defs[spec.Name] = from
- }
- }
-}
-
-// checkInPackageBlock performs safety checks for renames of
-// func/var/const/type objects in the package block.
-func (r *renamer) checkInPackageBlock(from types.Object) {
- // Check that there are no references to the name from another
- // package if the renaming would make it unexported.
- if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
- for pkg, info := range r.packages {
- if pkg == from.Pkg() {
- continue
- }
- if id := someUse(info, from); id != nil &&
- !r.checkExport(id, pkg, from) {
- break
- }
- }
- }
-
- info := r.packages[from.Pkg()]
-
- // Check that in the package block, "init" is a function, and never referenced.
- if r.to == "init" {
- kind := objectKind(from)
- if kind == "func" {
- // Reject if intra-package references to it exist.
- for id, obj := range info.Uses {
- if obj == from {
- r.errorf(from.Pos(),
- "renaming this func %q to %q would make it a package initializer",
- from.Name(), r.to)
- r.errorf(id.Pos(), "\tbut references to it exist")
- break
- }
- }
- } else {
- r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
- kind, r.to)
- }
- }
-
- // Check for conflicts between package block and all file blocks.
- for _, f := range info.Files {
- fileScope := info.Info.Scopes[f]
- b, prev := fileScope.LookupParent(r.to, token.NoPos)
- if b == fileScope {
- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
- objectKind(from), from.Name(), r.to)
- r.errorf(prev.Pos(), "\twith this %s",
- objectKind(prev))
- return // since checkInPackageBlock would report redundant errors
- }
- }
-
- // Check for conflicts in lexical scope.
- if from.Exported() {
- for _, info := range r.packages {
- r.checkInLexicalScope(from, info)
- }
- } else {
- r.checkInLexicalScope(from, info)
- }
-}
-
-func (r *renamer) checkInLocalScope(from types.Object) {
- info := r.packages[from.Pkg()]
-
- // Is this object an implicit local var for a type switch?
- // Each case has its own var, whose position is the decl of y,
- // but Ident in that decl does not appear in the Uses map.
- //
- // switch y := x.(type) { // Defs[Ident(y)] is undefined
- // case int: print(y) // Implicits[CaseClause(int)] = Var(y_int)
- // case string: print(y) // Implicits[CaseClause(string)] = Var(y_string)
- // }
- //
- var isCaseVar bool
- for syntax, obj := range info.Implicits {
- if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() {
- isCaseVar = true
- r.check(obj)
- }
- }
-
- r.checkInLexicalScope(from, info)
-
- // Finally, if this was a type switch, change the variable y.
- if isCaseVar {
- _, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
- path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...]
- }
-}
-
-// checkInLexicalScope performs safety checks that a renaming does not
-// change the lexical reference structure of the specified package.
-//
-// For objects in lexical scope, there are three kinds of conflicts:
-// same-, sub-, and super-block conflicts. We will illustrate all three
-// using this example:
-//
-// var x int
-// var z int
-//
-// func f(y int) {
-// print(x)
-// print(y)
-// }
-//
-// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
-// with the new name already exists, defined in the same lexical block
-// as the old object.
-//
-// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
-// a reference to x from within (what would become) a hole in its scope.
-// The definition of y in an (inner) sub-block would cast a shadow in
-// the scope of the renamed variable.
-//
-// Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the
-// converse situation: there is an existing definition of the new name
-// (x) in an (enclosing) super-block, and the renaming would create a
-// hole in its scope, within which there exist references to it. The
-// new name casts a shadow in scope of the existing definition of x in
-// the super-block.
-//
-// Removing the old name (and all references to it) is always safe, and
-// requires no checks.
-//
-func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
- b := from.Parent() // the block defining the 'from' object
- if b != nil {
- toBlock, to := b.LookupParent(r.to, from.Parent().End())
- if toBlock == b {
- // same-block conflict
- r.errorf(from.Pos(), "renaming this %s %q to %q",
- objectKind(from), from.Name(), r.to)
- r.errorf(to.Pos(), "\tconflicts with %s in same block",
- objectKind(to))
- return
- } else if toBlock != nil {
- // Check for super-block conflict.
- // The name r.to is defined in a superblock.
- // Is that name referenced from within this block?
- forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool {
- _, obj := lexicalLookup(block, from.Name(), id.Pos())
- if obj == from {
- // super-block conflict
- r.errorf(from.Pos(), "renaming this %s %q to %q",
- objectKind(from), from.Name(), r.to)
- r.errorf(id.Pos(), "\twould shadow this reference")
- r.errorf(to.Pos(), "\tto the %s declared here",
- objectKind(to))
- return false // stop
- }
- return true
- })
- }
- }
-
- // Check for sub-block conflict.
- // Is there an intervening definition of r.to between
- // the block defining 'from' and some reference to it?
- forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool {
- // Find the block that defines the found reference.
- // It may be an ancestor.
- fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
-
- // See what r.to would resolve to in the same scope.
- toBlock, to := lexicalLookup(block, r.to, id.Pos())
- if to != nil {
- // sub-block conflict
- if deeper(toBlock, fromBlock) {
- r.errorf(from.Pos(), "renaming this %s %q to %q",
- objectKind(from), from.Name(), r.to)
- r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
- r.errorf(to.Pos(), "\tby this intervening %s definition",
- objectKind(to))
- return false // stop
- }
- }
- return true
- })
-
- // Renaming a type that is used as an embedded field
- // requires renaming the field too. e.g.
- // type T int // if we rename this to U..
- // var s struct {T}
- // print(s.T) // ...this must change too
- if _, ok := from.(*types.TypeName); ok {
- for id, obj := range info.Uses {
- if obj == from {
- if field := info.Defs[id]; field != nil {
- r.check(field)
- }
- }
- }
- }
-}
-
-// lexicalLookup is like (*types.Scope).LookupParent but respects the
-// environment visible at pos. It assumes the relative position
-// information is correct with each file.
-func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
- for b := block; b != nil; b = b.Parent() {
- obj := b.Lookup(name)
- // The scope of a package-level object is the entire package,
- // so ignore pos in that case.
- // No analogous clause is needed for file-level objects
- // since no reference can appear before an import decl.
- if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
- return b, obj
- }
- }
- return nil, nil
-}
-
-// deeper reports whether block x is lexically deeper than y.
-func deeper(x, y *types.Scope) bool {
- if x == y || x == nil {
- return false
- } else if y == nil {
- return true
- } else {
- return deeper(x.Parent(), y.Parent())
- }
-}
-
-// forEachLexicalRef calls fn(id, block) for each identifier id in package
-// info that is a reference to obj in lexical scope. block is the
-// lexical block enclosing the reference. If fn returns false the
-// iteration is terminated and findLexicalRefs returns false.
-func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
- ok := true
- var stack []ast.Node
-
- var visit func(n ast.Node) bool
- visit = func(n ast.Node) bool {
- if n == nil {
- stack = stack[:len(stack)-1] // pop
- return false
- }
- if !ok {
- return false // bail out
- }
-
- stack = append(stack, n) // push
- switch n := n.(type) {
- case *ast.Ident:
- if info.Uses[n] == obj {
- block := enclosingBlock(&info.Info, stack)
- if !fn(n, block) {
- ok = false
- }
- }
- return visit(nil) // pop stack
-
- case *ast.SelectorExpr:
- // don't visit n.Sel
- ast.Inspect(n.X, visit)
- return visit(nil) // pop stack, don't descend
-
- case *ast.CompositeLit:
- // Handle recursion ourselves for struct literals
- // so we don't visit field identifiers.
- tv := info.Types[n]
- if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
- if n.Type != nil {
- ast.Inspect(n.Type, visit)
- }
- for _, elt := range n.Elts {
- if kv, ok := elt.(*ast.KeyValueExpr); ok {
- ast.Inspect(kv.Value, visit)
- } else {
- ast.Inspect(elt, visit)
- }
- }
- return visit(nil) // pop stack, don't descend
- }
- }
- return true
- }
-
- for _, f := range info.Files {
- ast.Inspect(f, visit)
- if len(stack) != 0 {
- panic(stack)
- }
- if !ok {
- break
- }
- }
- return ok
-}
-
-// enclosingBlock returns the innermost block enclosing the specified
-// AST node, specified in the form of a path from the root of the file,
-// [file...n].
-func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
- for i := range stack {
- n := stack[len(stack)-1-i]
- // For some reason, go/types always associates a
- // function's scope with its FuncType.
- // TODO(adonovan): feature or a bug?
- switch f := n.(type) {
- case *ast.FuncDecl:
- n = f.Type
- case *ast.FuncLit:
- n = f.Type
- }
- if b := info.Scopes[n]; b != nil {
- return b
- }
- }
- panic("no Scope for *ast.File")
-}
-
-func (r *renamer) checkLabel(label *types.Label) {
- // Check there are no identical labels in the function's label block.
- // (Label blocks don't nest, so this is easy.)
- if prev := label.Parent().Lookup(r.to); prev != nil {
- r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name())
- r.errorf(prev.Pos(), "\twould conflict with this one")
- }
-}
-
-// checkStructField checks that the field renaming will not cause
-// conflicts at its declaration, or ambiguity or changes to any selection.
-func (r *renamer) checkStructField(from *types.Var) {
- // Check that the struct declaration is free of field conflicts,
- // and field/method conflicts.
-
- // go/types offers no easy way to get from a field (or interface
- // method) to its declaring struct (or interface), so we must
- // ascend the AST.
- info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
- // path matches this pattern:
- // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
-
- // Ascend to FieldList.
- var i int
- for {
- if _, ok := path[i].(*ast.FieldList); ok {
- break
- }
- i++
- }
- i++
- tStruct := path[i].(*ast.StructType)
- i++
- // Ascend past parens (unlikely).
- for {
- _, ok := path[i].(*ast.ParenExpr)
- if !ok {
- break
- }
- i++
- }
- if spec, ok := path[i].(*ast.TypeSpec); ok {
- // This struct is also a named type.
- // We must check for direct (non-promoted) field/field
- // and method/field conflicts.
- named := info.Defs[spec.Name].Type()
- prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to)
- if len(indices) == 1 {
- r.errorf(from.Pos(), "renaming this field %q to %q",
- from.Name(), r.to)
- r.errorf(prev.Pos(), "\twould conflict with this %s",
- objectKind(prev))
- return // skip checkSelections to avoid redundant errors
- }
- } else {
- // This struct is not a named type.
- // We need only check for direct (non-promoted) field/field conflicts.
- T := info.Types[tStruct].Type.Underlying().(*types.Struct)
- for i := 0; i < T.NumFields(); i++ {
- if prev := T.Field(i); prev.Name() == r.to {
- r.errorf(from.Pos(), "renaming this field %q to %q",
- from.Name(), r.to)
- r.errorf(prev.Pos(), "\twould conflict with this field")
- return // skip checkSelections to avoid redundant errors
- }
- }
- }
-
- // Renaming an anonymous field requires renaming the type too. e.g.
- // print(s.T) // if we rename T to U,
- // type T int // this and
- // var s struct {T} // this must change too.
- if from.Anonymous() {
- if named, ok := from.Type().(*types.Named); ok {
- r.check(named.Obj())
- } else if named, ok := deref(from.Type()).(*types.Named); ok {
- r.check(named.Obj())
- }
- }
-
- // Check integrity of existing (field and method) selections.
- r.checkSelections(from)
-}
-
-// checkSelection checks that all uses and selections that resolve to
-// the specified object would continue to do so after the renaming.
-func (r *renamer) checkSelections(from types.Object) {
- for pkg, info := range r.packages {
- if id := someUse(info, from); id != nil {
- if !r.checkExport(id, pkg, from) {
- return
- }
- }
-
- for syntax, sel := range info.Selections {
- // There may be extant selections of only the old
- // name or only the new name, so we must check both.
- // (If neither, the renaming is sound.)
- //
- // In both cases, we wish to compare the lengths
- // of the implicit field path (Selection.Index)
- // to see if the renaming would change it.
- //
- // If a selection that resolves to 'from', when renamed,
- // would yield a path of the same or shorter length,
- // this indicates ambiguity or a changed referent,
- // analogous to same- or sub-block lexical conflict.
- //
- // If a selection using the name 'to' would
- // yield a path of the same or shorter length,
- // this indicates ambiguity or shadowing,
- // analogous to same- or super-block lexical conflict.
-
- // TODO(adonovan): fix: derive from Types[syntax.X].Mode
- // TODO(adonovan): test with pointer, value, addressable value.
- isAddressable := true
-
- if sel.Obj() == from {
- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil {
- // Renaming this existing selection of
- // 'from' may block access to an existing
- // type member named 'to'.
- delta := len(indices) - len(sel.Index())
- if delta > 0 {
- continue // no ambiguity
- }
- r.selectionConflict(from, delta, syntax, obj)
- return
- }
-
- } else if sel.Obj().Name() == r.to {
- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from {
- // Renaming 'from' may cause this existing
- // selection of the name 'to' to change
- // its meaning.
- delta := len(indices) - len(sel.Index())
- if delta > 0 {
- continue // no ambiguity
- }
- r.selectionConflict(from, -delta, syntax, sel.Obj())
- return
- }
- }
- }
- }
-}
-
-func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) {
- r.errorf(from.Pos(), "renaming this %s %q to %q",
- objectKind(from), from.Name(), r.to)
-
- switch {
- case delta < 0:
- // analogous to sub-block conflict
- r.errorf(syntax.Sel.Pos(),
- "\twould change the referent of this selection")
- r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))
- case delta == 0:
- // analogous to same-block conflict
- r.errorf(syntax.Sel.Pos(),
- "\twould make this reference ambiguous")
- r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))
- case delta > 0:
- // analogous to super-block conflict
- r.errorf(syntax.Sel.Pos(),
- "\twould shadow this selection")
- r.errorf(obj.Pos(), "\tof the %s declared here",
- objectKind(obj))
- }
-}
-
-// checkMethod performs safety checks for renaming a method.
-// There are three hazards:
-// - declaration conflicts
-// - selection ambiguity/changes
-// - entailed renamings of assignable concrete/interface types.
-// We reject renamings initiated at concrete methods if it would
-// change the assignability relation. For renamings of abstract
-// methods, we rename all methods transitively coupled to it via
-// assignability.
-func (r *renamer) checkMethod(from *types.Func) {
- // e.g. error.Error
- if from.Pkg() == nil {
- r.errorf(from.Pos(), "you cannot rename built-in method %s", from)
- return
- }
-
- // ASSIGNABILITY: We reject renamings of concrete methods that
- // would break a 'satisfy' constraint; but renamings of abstract
- // methods are allowed to proceed, and we rename affected
- // concrete and abstract methods as necessary. It is the
- // initial method that determines the policy.
-
- // Check for conflict at point of declaration.
- // Check to ensure preservation of assignability requirements.
- R := recv(from).Type()
- if isInterface(R) {
- // Abstract method
-
- // declaration
- prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to)
- if prev != nil {
- r.errorf(from.Pos(), "renaming this interface method %q to %q",
- from.Name(), r.to)
- r.errorf(prev.Pos(), "\twould conflict with this method")
- return
- }
-
- // Check all interfaces that embed this one for
- // declaration conflicts too.
- for _, info := range r.packages {
- // Start with named interface types (better errors)
- for _, obj := range info.Defs {
- if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
- f, _, _ := types.LookupFieldOrMethod(
- obj.Type(), false, from.Pkg(), from.Name())
- if f == nil {
- continue
- }
- t, _, _ := types.LookupFieldOrMethod(
- obj.Type(), false, from.Pkg(), r.to)
- if t == nil {
- continue
- }
- r.errorf(from.Pos(), "renaming this interface method %q to %q",
- from.Name(), r.to)
- r.errorf(t.Pos(), "\twould conflict with this method")
- r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name())
- }
- }
-
- // Now look at all literal interface types (includes named ones again).
- for e, tv := range info.Types {
- if e, ok := e.(*ast.InterfaceType); ok {
- _ = e
- _ = tv.Type.(*types.Interface)
- // TODO(adonovan): implement same check as above.
- }
- }
- }
-
- // assignability
- //
- // Find the set of concrete or abstract methods directly
- // coupled to abstract method 'from' by some
- // satisfy.Constraint, and rename them too.
- for key := range r.satisfy() {
- // key = (lhs, rhs) where lhs is always an interface.
-
- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
- if lsel == nil {
- continue
- }
- rmethods := r.msets.MethodSet(key.RHS)
- rsel := rmethods.Lookup(from.Pkg(), from.Name())
- if rsel == nil {
- continue
- }
-
- // If both sides have a method of this name,
- // and one of them is m, the other must be coupled.
- var coupled *types.Func
- switch from {
- case lsel.Obj():
- coupled = rsel.Obj().(*types.Func)
- case rsel.Obj():
- coupled = lsel.Obj().(*types.Func)
- default:
- continue
- }
-
- // We must treat concrete-to-interface
- // constraints like an implicit selection C.f of
- // each interface method I.f, and check that the
- // renaming leaves the selection unchanged and
- // unambiguous.
- //
- // Fun fact: the implicit selection of C.f
- // type I interface{f()}
- // type C struct{I}
- // func (C) g()
- // var _ I = C{} // here
- // yields abstract method I.f. This can make error
- // messages less than obvious.
- //
- if !isInterface(key.RHS) {
- // The logic below was derived from checkSelections.
-
- rtosel := rmethods.Lookup(from.Pkg(), r.to)
- if rtosel != nil {
- rto := rtosel.Obj().(*types.Func)
- delta := len(rsel.Index()) - len(rtosel.Index())
- if delta < 0 {
- continue // no ambiguity
- }
-
- // TODO(adonovan): record the constraint's position.
- keyPos := token.NoPos
-
- r.errorf(from.Pos(), "renaming this method %q to %q",
- from.Name(), r.to)
- if delta == 0 {
- // analogous to same-block conflict
- r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
- r.to, key.RHS, key.LHS)
- r.errorf(rto.Pos(), "\twith (%s).%s",
- recv(rto).Type(), r.to)
- } else {
- // analogous to super-block conflict
- r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
- r.to, key.RHS, key.LHS)
- r.errorf(coupled.Pos(), "\tfrom (%s).%s",
- recv(coupled).Type(), r.to)
- r.errorf(rto.Pos(), "\tto (%s).%s",
- recv(rto).Type(), r.to)
- }
- return // one error is enough
- }
- }
-
- if !r.changeMethods {
- // This should be unreachable.
- r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from)
- r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled)
- r.errorf(from.Pos(), "\tPlease file a bug report")
- return
- }
-
- // Rename the coupled method to preserve assignability.
- r.check(coupled)
- }
- } else {
- // Concrete method
-
- // declaration
- prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to)
- if prev != nil && len(indices) == 1 {
- r.errorf(from.Pos(), "renaming this method %q to %q",
- from.Name(), r.to)
- r.errorf(prev.Pos(), "\twould conflict with this %s",
- objectKind(prev))
- return
- }
-
- // assignability
- //
- // Find the set of abstract methods coupled to concrete
- // method 'from' by some satisfy.Constraint, and rename
- // them too.
- //
- // Coupling may be indirect, e.g. I.f <-> C.f via type D.
- //
- // type I interface {f()}
- // type C int
- // type (C) f()
- // type D struct{C}
- // var _ I = D{}
- //
- for key := range r.satisfy() {
- // key = (lhs, rhs) where lhs is always an interface.
- if isInterface(key.RHS) {
- continue
- }
- rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
- if rsel == nil || rsel.Obj() != from {
- continue // rhs does not have the method
- }
- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
- if lsel == nil {
- continue
- }
- imeth := lsel.Obj().(*types.Func)
-
- // imeth is the abstract method (e.g. I.f)
- // and key.RHS is the concrete coupling type (e.g. D).
- if !r.changeMethods {
- r.errorf(from.Pos(), "renaming this method %q to %q",
- from.Name(), r.to)
- var pos token.Pos
- var iface string
-
- I := recv(imeth).Type()
- if named, ok := I.(*types.Named); ok {
- pos = named.Obj().Pos()
- iface = "interface " + named.Obj().Name()
- } else {
- pos = from.Pos()
- iface = I.String()
- }
- r.errorf(pos, "\twould make %s no longer assignable to %s",
- key.RHS, iface)
- r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
- I, from.Name())
- return // one error is enough
- }
-
- // Rename the coupled interface method to preserve assignability.
- r.check(imeth)
- }
- }
-
- // Check integrity of existing (field and method) selections.
- // We skip this if there were errors above, to avoid redundant errors.
- r.checkSelections(from)
-}
-
-func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
- // Reject cross-package references if r.to is unexported.
- // (Such references may be qualified identifiers or field/method
- // selections.)
- if !ast.IsExported(r.to) && pkg != from.Pkg() {
- r.errorf(from.Pos(),
- "renaming this %s %q to %q would make it unexported",
- objectKind(from), from.Name(), r.to)
- r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
- pkg.Path())
- return false
- }
- return true
-}
-
-// satisfy returns the set of interface satisfaction constraints.
-func (r *renamer) satisfy() map[satisfy.Constraint]bool {
- if r.satisfyConstraints == nil {
- // Compute on demand: it's expensive.
- var f satisfy.Finder
- for _, info := range r.packages {
- f.Find(&info.Info, info.Files)
- }
- r.satisfyConstraints = f.Result
- }
- return r.satisfyConstraints
-}
-
-// -- helpers ----------------------------------------------------------
-
-// recv returns the method's receiver.
-func recv(meth *types.Func) *types.Var {
- return meth.Type().(*types.Signature).Recv()
-}
-
-// someUse returns an arbitrary use of obj within info.
-func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident {
- for id, o := range info.Uses {
- if o == obj {
- return id
- }
- }
- return nil
-}
-
-// -- Plundered from golang.org/x/tools/go/ssa -----------------
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }
-
-func deref(typ types.Type) types.Type {
- if p, _ := typ.(*types.Pointer); p != nil {
- return p.Elem()
- }
- return typ
-}
diff --git a/refactor/rename/rename14.go b/refactor/rename/rename14.go
deleted file mode 100644
index e4ccc061ab..0000000000
--- a/refactor/rename/rename14.go
+++ /dev/null
@@ -1,510 +0,0 @@
-// 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 rename contains the implementation of the 'gorename' command
-// whose main function is in golang.org/x/tools/cmd/gorename.
-// See the Usage constant for the command documentation.
-package rename // import "golang.org/x/tools/refactor/rename"
-
-import (
- "bytes"
- "errors"
- "fmt"
- "go/ast"
- "go/build"
- "go/format"
- "go/parser"
- "go/token"
- "io"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path"
- "sort"
- "strconv"
- "strings"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
- "golang.org/x/tools/refactor/importgraph"
- "golang.org/x/tools/refactor/satisfy"
-)
-
-const Usage = `gorename: precise type-safe renaming of identifiers in Go source code.
-
-Usage:
-
- gorename (-from | -offset :#) -to [-force]
-
-You must specify the object (named entity) to rename using the -offset
-or -from flag. Exactly one must be specified.
-
-Flags:
-
--offset specifies the filename and byte offset of an identifier to rename.
- This form is intended for use by text editors.
-
--from specifies the object to rename using a query notation;
- This form is intended for interactive use at the command line.
- A legal -from query has one of the following forms:
-
- "encoding/json".Decoder.Decode method of package-level named type
- (*"encoding/json".Decoder).Decode ditto, alternative syntax
- "encoding/json".Decoder.buf field of package-level named struct type
- "encoding/json".HTMLEscape package member (const, func, var, type)
- "encoding/json".Decoder.Decode::x local object x within a method
- "encoding/json".HTMLEscape::x local object x within a function
- "encoding/json"::x object x anywhere within a package
- json.go::x object x within file json.go
-
- Double-quotes must be escaped when writing a shell command.
- Quotes may be omitted for single-segment import paths such as "fmt".
-
- For methods, the parens and '*' on the receiver type are both
- optional.
-
- It is an error if one of the ::x queries matches multiple
- objects.
-
--to the new name.
-
--force causes the renaming to proceed even if conflicts were reported.
- The resulting program may be ill-formed, or experience a change
- in behaviour.
-
- WARNING: this flag may even cause the renaming tool to crash.
- (In due course this bug will be fixed by moving certain
- analyses into the type-checker.)
-
--d display diffs instead of rewriting files
-
--v enables verbose logging.
-
-gorename automatically computes the set of packages that might be
-affected. For a local renaming, this is just the package specified by
--from or -offset, but for a potentially exported name, gorename scans
-the workspace ($GOROOT and $GOPATH).
-
-gorename rejects renamings of concrete methods that would change the
-assignability relation between types and interfaces. If the interface
-change was intentional, initiate the renaming at the interface method.
-
-gorename rejects any renaming that would create a conflict at the point
-of declaration, or a reference conflict (ambiguity or shadowing), or
-anything else that could cause the resulting program not to compile.
-
-
-Examples:
-
-$ gorename -offset file.go:#123 -to foo
-
- Rename the object whose identifier is at byte offset 123 within file file.go.
-
-$ gorename -from '"bytes".Buffer.Len' -to Size
-
- Rename the "Len" method of the *bytes.Buffer type to "Size".
-
----- TODO ----
-
-Correctness:
-- handle dot imports correctly
-- document limitations (reflection, 'implements' algorithm).
-- sketch a proof of exhaustiveness.
-
-Features:
-- support running on packages specified as *.go files on the command line
-- support running on programs containing errors (loader.Config.AllowErrors)
-- allow users to specify a scope other than "global" (to avoid being
- stuck by neglected packages in $GOPATH that don't build).
-- support renaming the package clause (no object)
-- support renaming an import path (no ident or object)
- (requires filesystem + SCM updates).
-- detect and reject edits to autogenerated files (cgo, protobufs)
- and optionally $GOROOT packages.
-- report all conflicts, or at least all qualitatively distinct ones.
- Sometimes we stop to avoid redundancy, but
- it may give a disproportionate sense of safety in -force mode.
-- support renaming all instances of a pattern, e.g.
- all receiver vars of a given type,
- all local variables of a given type,
- all PkgNames for a given package.
-- emit JSON output for other editors and tools.
-`
-
-var (
- // Force enables patching of the source files even if conflicts were reported.
- // The resulting program may be ill-formed.
- // It may even cause gorename to crash. TODO(adonovan): fix that.
- Force bool
-
- // Diff causes the tool to display diffs instead of rewriting files.
- Diff bool
-
- // DiffCmd specifies the diff command used by the -d feature.
- // (The command must accept a -u flag and two filename arguments.)
- DiffCmd = "diff"
-
- // ConflictError is returned by Main when it aborts the renaming due to conflicts.
- // (It is distinguished because the interesting errors are the conflicts themselves.)
- ConflictError = errors.New("renaming aborted due to conflicts")
-
- // Verbose enables extra logging.
- Verbose bool
-)
-
-var stdout io.Writer = os.Stdout
-
-type renamer struct {
- iprog *loader.Program
- objsToUpdate map[types.Object]bool
- hadConflicts bool
- to string
- satisfyConstraints map[satisfy.Constraint]bool
- packages map[*types.Package]*loader.PackageInfo // subset of iprog.AllPackages to inspect
- msets typeutil.MethodSetCache
- changeMethods bool
-}
-
-var reportError = func(posn token.Position, message string) {
- fmt.Fprintf(os.Stderr, "%s: %s\n", posn, message)
-}
-
-// importName renames imports of the package with the given path in
-// the given package. If fromName is not empty, only imports as
-// fromName will be renamed. If the renaming would lead to a conflict,
-// the file is left unchanged.
-func importName(iprog *loader.Program, info *loader.PackageInfo, fromPath, fromName, to string) error {
- for _, f := range info.Files {
- var from types.Object
- for _, imp := range f.Imports {
- importPath, _ := strconv.Unquote(imp.Path.Value)
- importName := path.Base(importPath)
- if imp.Name != nil {
- importName = imp.Name.Name
- }
- if importPath == fromPath && (fromName == "" || importName == fromName) {
- from = info.Implicits[imp]
- break
- }
- }
- if from == nil {
- continue
- }
- r := renamer{
- iprog: iprog,
- objsToUpdate: make(map[types.Object]bool),
- to: to,
- packages: map[*types.Package]*loader.PackageInfo{info.Pkg: info},
- }
- r.check(from)
- if r.hadConflicts {
- continue // ignore errors; leave the existing name
- }
- if err := r.update(); err != nil {
- return err
- }
- }
- return nil
-}
-
-func Main(ctxt *build.Context, offsetFlag, fromFlag, to string) error {
- // -- Parse the -from or -offset specifier ----------------------------
-
- if (offsetFlag == "") == (fromFlag == "") {
- return fmt.Errorf("exactly one of the -from and -offset flags must be specified")
- }
-
- if !isValidIdentifier(to) {
- return fmt.Errorf("-to %q: not a valid identifier", to)
- }
-
- if Diff {
- defer func(saved func(string, []byte) error) { writeFile = saved }(writeFile)
- writeFile = diff
- }
-
- var spec *spec
- var err error
- if fromFlag != "" {
- spec, err = parseFromFlag(ctxt, fromFlag)
- } else {
- spec, err = parseOffsetFlag(ctxt, offsetFlag)
- }
- if err != nil {
- return err
- }
-
- if spec.fromName == to {
- return fmt.Errorf("the old and new names are the same: %s", to)
- }
-
- // -- Load the program consisting of the initial package -------------
-
- iprog, err := loadProgram(ctxt, map[string]bool{spec.pkg: true})
- if err != nil {
- return err
- }
-
- fromObjects, err := findFromObjects(iprog, spec)
- if err != nil {
- return err
- }
-
- // -- Load a larger program, for global renamings ---------------------
-
- if requiresGlobalRename(fromObjects, to) {
- // For a local refactoring, we needn't load more
- // packages, but if the renaming affects the package's
- // API, we we must load all packages that depend on the
- // package defining the object, plus their tests.
-
- if Verbose {
- log.Print("Potentially global renaming; scanning workspace...")
- }
-
- // Scan the workspace and build the import graph.
- _, rev, errors := importgraph.Build(ctxt)
- if len(errors) > 0 {
- // With a large GOPATH tree, errors are inevitable.
- // Report them but proceed.
- fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n")
- for path, err := range errors {
- fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err)
- }
- }
-
- // Enumerate the set of potentially affected packages.
- affectedPackages := make(map[string]bool)
- for _, obj := range fromObjects {
- // External test packages are never imported,
- // so they will never appear in the graph.
- for path := range rev.Search(obj.Pkg().Path()) {
- affectedPackages[path] = true
- }
- }
-
- // TODO(adonovan): allow the user to specify the scope,
- // or -ignore patterns? Computing the scope when we
- // don't (yet) support inputs containing errors can make
- // the tool rather brittle.
-
- // Re-load the larger program.
- iprog, err = loadProgram(ctxt, affectedPackages)
- if err != nil {
- return err
- }
-
- fromObjects, err = findFromObjects(iprog, spec)
- if err != nil {
- return err
- }
- }
-
- // -- Do the renaming -------------------------------------------------
-
- r := renamer{
- iprog: iprog,
- objsToUpdate: make(map[types.Object]bool),
- to: to,
- packages: make(map[*types.Package]*loader.PackageInfo),
- }
-
- // A renaming initiated at an interface method indicates the
- // intention to rename abstract and concrete methods as needed
- // to preserve assignability.
- for _, obj := range fromObjects {
- if obj, ok := obj.(*types.Func); ok {
- recv := obj.Type().(*types.Signature).Recv()
- if recv != nil && isInterface(recv.Type().Underlying()) {
- r.changeMethods = true
- break
- }
- }
- }
-
- // Only the initially imported packages (iprog.Imported) and
- // their external tests (iprog.Created) should be inspected or
- // modified, as only they have type-checked functions bodies.
- // The rest are just dependencies, needed only for package-level
- // type information.
- for _, info := range iprog.Imported {
- r.packages[info.Pkg] = info
- }
- for _, info := range iprog.Created { // (tests)
- r.packages[info.Pkg] = info
- }
-
- for _, from := range fromObjects {
- r.check(from)
- }
- if r.hadConflicts && !Force {
- return ConflictError
- }
- return r.update()
-}
-
-// loadProgram loads the specified set of packages (plus their tests)
-// and all their dependencies, from source, through the specified build
-// context. Only packages in pkgs will have their functions bodies typechecked.
-func loadProgram(ctxt *build.Context, pkgs map[string]bool) (*loader.Program, error) {
- conf := loader.Config{
- Build: ctxt,
- ParserMode: parser.ParseComments,
-
- // TODO(adonovan): enable this. Requires making a lot of code more robust!
- AllowErrors: false,
- }
-
- // Optimization: don't type-check the bodies of functions in our
- // dependencies, since we only need exported package members.
- conf.TypeCheckFuncBodies = func(p string) bool {
- return pkgs[p] || pkgs[strings.TrimSuffix(p, "_test")]
- }
-
- if Verbose {
- var list []string
- for pkg := range pkgs {
- list = append(list, pkg)
- }
- sort.Strings(list)
- for _, pkg := range list {
- log.Printf("Loading package: %s", pkg)
- }
- }
-
- for pkg := range pkgs {
- conf.ImportWithTests(pkg)
- }
- return conf.Load()
-}
-
-// requiresGlobalRename reports whether this renaming could potentially
-// affect other packages in the Go workspace.
-func requiresGlobalRename(fromObjects []types.Object, to string) bool {
- var tfm bool
- for _, from := range fromObjects {
- if from.Exported() {
- return true
- }
- switch objectKind(from) {
- case "type", "field", "method":
- tfm = true
- }
- }
- if ast.IsExported(to) && tfm {
- // A global renaming may be necessary even if we're
- // exporting a previous unexported name, since if it's
- // the name of a type, field or method, this could
- // change selections in other packages.
- // (We include "type" in this list because a type
- // used as an embedded struct field entails a field
- // renaming.)
- return true
- }
- return false
-}
-
-// update updates the input files.
-func (r *renamer) update() error {
- // We use token.File, not filename, since a file may appear to
- // belong to multiple packages and be parsed more than once.
- // token.File captures this distinction; filename does not.
- var nidents int
- var filesToUpdate = make(map[*token.File]bool)
- for _, info := range r.packages {
- // Mutate the ASTs and note the filenames.
- for id, obj := range info.Defs {
- if r.objsToUpdate[obj] {
- nidents++
- id.Name = r.to
- filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
- }
- }
- for id, obj := range info.Uses {
- if r.objsToUpdate[obj] {
- nidents++
- id.Name = r.to
- filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
- }
- }
- }
-
- // TODO(adonovan): don't rewrite cgo + generated files.
- var nerrs, npkgs int
- for _, info := range r.packages {
- first := true
- for _, f := range info.Files {
- tokenFile := r.iprog.Fset.File(f.Pos())
- if filesToUpdate[tokenFile] {
- if first {
- npkgs++
- first = false
- if Verbose {
- log.Printf("Updating package %s", info.Pkg.Path())
- }
- }
-
- filename := tokenFile.Name()
- var buf bytes.Buffer
- if err := format.Node(&buf, r.iprog.Fset, f); err != nil {
- log.Printf("failed to pretty-print syntax tree: %v", err)
- nerrs++
- continue
- }
- if err := writeFile(filename, buf.Bytes()); err != nil {
- log.Print(err)
- nerrs++
- }
- }
- }
- }
- if !Diff {
- fmt.Printf("Renamed %d occurrence%s in %d file%s in %d package%s.\n",
- nidents, plural(nidents),
- len(filesToUpdate), plural(len(filesToUpdate)),
- npkgs, plural(npkgs))
- }
- if nerrs > 0 {
- return fmt.Errorf("failed to rewrite %d file%s", nerrs, plural(nerrs))
- }
- return nil
-}
-
-func plural(n int) string {
- if n != 1 {
- return "s"
- }
- return ""
-}
-
-// writeFile is a seam for testing and for the -d flag.
-var writeFile = reallyWriteFile
-
-func reallyWriteFile(filename string, content []byte) error {
- return ioutil.WriteFile(filename, content, 0644)
-}
-
-func diff(filename string, content []byte) error {
- renamed := fmt.Sprintf("%s.%d.renamed", filename, os.Getpid())
- if err := ioutil.WriteFile(renamed, content, 0644); err != nil {
- return err
- }
- defer os.Remove(renamed)
-
- diff, err := exec.Command(DiffCmd, "-u", filename, renamed).CombinedOutput()
- if len(diff) > 0 {
- // diff exits with a non-zero status when the files don't match.
- // Ignore that failure as long as we get output.
- stdout.Write(diff)
- return nil
- }
- if err != nil {
- return fmt.Errorf("computing diff: %v", err)
- }
- return nil
-}
diff --git a/refactor/rename/spec14.go b/refactor/rename/spec14.go
deleted file mode 100644
index 7634ae8fb0..0000000000
--- a/refactor/rename/spec14.go
+++ /dev/null
@@ -1,568 +0,0 @@
-// 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 rename
-
-// This file contains logic related to specifying a renaming: parsing of
-// the flags as a form of query, and finding the object(s) it denotes.
-// See Usage for flag details.
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/build"
- "go/parser"
- "go/token"
- "log"
- "os"
- "path/filepath"
- "strconv"
- "strings"
-
- "golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
-)
-
-// A spec specifies an entity to rename.
-//
-// It is populated from an -offset flag or -from query;
-// see Usage for the allowed -from query forms.
-//
-type spec struct {
- // pkg is the package containing the position
- // specified by the -from or -offset flag.
- // If filename == "", our search for the 'from' entity
- // is restricted to this package.
- pkg string
-
- // The original name of the entity being renamed.
- // If the query had a ::from component, this is that;
- // otherwise it's the last segment, e.g.
- // (encoding/json.Decoder).from
- // encoding/json.from
- fromName string
-
- // -- The remaining fields are private to this file. All are optional. --
-
- // The query's ::x suffix, if any.
- searchFor string
-
- // e.g. "Decoder" in "(encoding/json.Decoder).fieldOrMethod"
- // or "encoding/json.Decoder
- pkgMember string
-
- // e.g. fieldOrMethod in "(encoding/json.Decoder).fieldOrMethod"
- typeMember string
-
- // Restricts the query to this file.
- // Implied by -from="file.go::x" and -offset flags.
- filename string
-
- // Byte offset of the 'from' identifier within the file named 'filename'.
- // -offset mode only.
- offset int
-}
-
-// parseFromFlag interprets the "-from" flag value as a renaming specification.
-// See Usage in rename.go for valid formats.
-func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
- var spec spec
- var main string // sans "::x" suffix
- switch parts := strings.Split(fromFlag, "::"); len(parts) {
- case 1:
- main = parts[0]
- case 2:
- main = parts[0]
- spec.searchFor = parts[1]
- if parts[1] == "" {
- // error
- }
- default:
- return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
- }
-
- if strings.HasSuffix(main, ".go") {
- // main is "filename.go"
- if spec.searchFor == "" {
- return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
- }
- spec.filename = main
- if !buildutil.FileExists(ctxt, spec.filename) {
- return nil, fmt.Errorf("no such file: %s", spec.filename)
- }
-
- bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
- if err != nil {
- return nil, err
- }
- spec.pkg = bp.ImportPath
-
- } else {
- // main is one of:
- // "importpath"
- // "importpath".member
- // (*"importpath".type).fieldormethod (parens and star optional)
- if err := parseObjectSpec(&spec, main); err != nil {
- return nil, err
- }
- }
-
- if spec.searchFor != "" {
- spec.fromName = spec.searchFor
- }
-
- cwd, err := os.Getwd()
- if err != nil {
- return nil, err
- }
-
- // Sanitize the package.
- bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
- if err != nil {
- return nil, fmt.Errorf("can't find package %q", spec.pkg)
- }
- spec.pkg = bp.ImportPath
-
- if !isValidIdentifier(spec.fromName) {
- return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
- }
-
- if Verbose {
- log.Printf("-from spec: %+v", spec)
- }
-
- return &spec, nil
-}
-
-// parseObjectSpec parses main as one of the non-filename forms of
-// object specification.
-func parseObjectSpec(spec *spec, main string) error {
- // Parse main as a Go expression, albeit a strange one.
- e, _ := parser.ParseExpr(main)
-
- if pkg := parseImportPath(e); pkg != "" {
- // e.g. bytes or "encoding/json": a package
- spec.pkg = pkg
- if spec.searchFor == "" {
- return fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
- main, main)
- }
- return nil
- }
-
- if e, ok := e.(*ast.SelectorExpr); ok {
- x := unparen(e.X)
-
- // Strip off star constructor, if any.
- if star, ok := x.(*ast.StarExpr); ok {
- x = star.X
- }
-
- if pkg := parseImportPath(x); pkg != "" {
- // package member e.g. "encoding/json".HTMLEscape
- spec.pkg = pkg // e.g. "encoding/json"
- spec.pkgMember = e.Sel.Name // e.g. "HTMLEscape"
- spec.fromName = e.Sel.Name
- return nil
- }
-
- if x, ok := x.(*ast.SelectorExpr); ok {
- // field/method of type e.g. ("encoding/json".Decoder).Decode
- y := unparen(x.X)
- if pkg := parseImportPath(y); pkg != "" {
- spec.pkg = pkg // e.g. "encoding/json"
- spec.pkgMember = x.Sel.Name // e.g. "Decoder"
- spec.typeMember = e.Sel.Name // e.g. "Decode"
- spec.fromName = e.Sel.Name
- return nil
- }
- }
- }
-
- return fmt.Errorf("-from %q: invalid expression", main)
-}
-
-// parseImportPath returns the import path of the package denoted by e.
-// Any import path may be represented as a string literal;
-// single-segment import paths (e.g. "bytes") may also be represented as
-// ast.Ident. parseImportPath returns "" for all other expressions.
-func parseImportPath(e ast.Expr) string {
- switch e := e.(type) {
- case *ast.Ident:
- return e.Name // e.g. bytes
-
- case *ast.BasicLit:
- if e.Kind == token.STRING {
- pkgname, _ := strconv.Unquote(e.Value)
- return pkgname // e.g. "encoding/json"
- }
- }
- return ""
-}
-
-// parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
-func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) {
- var spec spec
- // Validate -offset, e.g. file.go:#123
- parts := strings.Split(offsetFlag, ":#")
- if len(parts) != 2 {
- return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag)
- }
-
- spec.filename = parts[0]
- if !buildutil.FileExists(ctxt, spec.filename) {
- return nil, fmt.Errorf("no such file: %s", spec.filename)
- }
-
- bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
- if err != nil {
- return nil, err
- }
- spec.pkg = bp.ImportPath
-
- for _, r := range parts[1] {
- if !isDigit(r) {
- return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
- }
- }
- spec.offset, err = strconv.Atoi(parts[1])
- if err != nil {
- return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
- }
-
- // Parse the file and check there's an identifier at that offset.
- fset := token.NewFileSet()
- f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments)
- if err != nil {
- return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err)
- }
-
- id := identAtOffset(fset, f, spec.offset)
- if id == nil {
- return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag)
- }
-
- spec.fromName = id.Name
-
- return &spec, nil
-}
-
-var wd = func() string {
- wd, err := os.Getwd()
- if err != nil {
- panic("cannot get working directory: " + err.Error())
- }
- return wd
-}()
-
-// For source trees built with 'go build', the -from or -offset
-// spec identifies exactly one initial 'from' object to rename ,
-// but certain proprietary build systems allow a single file to
-// appear in multiple packages (e.g. the test package contains a
-// copy of its library), so there may be multiple objects for
-// the same source entity.
-
-func findFromObjects(iprog *loader.Program, spec *spec) ([]types.Object, error) {
- if spec.filename != "" {
- return findFromObjectsInFile(iprog, spec)
- }
-
- // Search for objects defined in specified package.
-
- // TODO(adonovan): the iprog.ImportMap has an entry {"main": ...}
- // for main packages, even though that's not an import path.
- // Seems like a bug.
- //
- // pkg := iprog.ImportMap[spec.pkg]
- // if pkg == nil {
- // return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen?
- // }
- // info := iprog.AllPackages[pkg]
-
- // Workaround: lookup by value.
- var info *loader.PackageInfo
- var pkg *types.Package
- for pkg, info = range iprog.AllPackages {
- if pkg.Path() == spec.pkg {
- break
- }
- }
- if info == nil {
- return nil, fmt.Errorf("package %q was not loaded", spec.pkg)
- }
-
- objects, err := findObjects(info, spec)
- if err != nil {
- return nil, err
- }
- if len(objects) > 1 {
- // ambiguous "*" scope query
- return nil, ambiguityError(iprog.Fset, objects)
- }
- return objects, nil
-}
-
-func findFromObjectsInFile(iprog *loader.Program, spec *spec) ([]types.Object, error) {
- var fromObjects []types.Object
- for _, info := range iprog.AllPackages {
- // restrict to specified filename
- // NB: under certain proprietary build systems, a given
- // filename may appear in multiple packages.
- for _, f := range info.Files {
- thisFile := iprog.Fset.File(f.Pos())
- if !sameFile(thisFile.Name(), spec.filename) {
- continue
- }
- // This package contains the query file.
-
- if spec.offset != 0 {
- // Search for a specific ident by file/offset.
- id := identAtOffset(iprog.Fset, f, spec.offset)
- if id == nil {
- // can't happen?
- return nil, fmt.Errorf("identifier not found")
- }
- obj := info.Uses[id]
- if obj == nil {
- obj = info.Defs[id]
- if obj == nil {
- // Ident without Object.
-
- // Package clause?
- pos := thisFile.Pos(spec.offset)
- _, path, _ := iprog.PathEnclosingInterval(pos, pos)
- if len(path) == 2 { // [Ident File]
- // TODO(adonovan): support this case.
- return nil, fmt.Errorf("cannot rename %q: renaming package clauses is not yet supported",
- path[1].(*ast.File).Name.Name)
- }
-
- // Implicit y in "switch y := x.(type) {"?
- if obj := typeSwitchVar(&info.Info, path); obj != nil {
- return []types.Object{obj}, nil
- }
-
- // Probably a type error.
- return nil, fmt.Errorf("cannot find object for %q", id.Name)
- }
- }
- if obj.Pkg() == nil {
- return nil, fmt.Errorf("cannot rename predeclared identifiers (%s)", obj)
-
- }
-
- fromObjects = append(fromObjects, obj)
- } else {
- // do a package-wide query
- objects, err := findObjects(info, spec)
- if err != nil {
- return nil, err
- }
-
- // filter results: only objects defined in thisFile
- var filtered []types.Object
- for _, obj := range objects {
- if iprog.Fset.File(obj.Pos()) == thisFile {
- filtered = append(filtered, obj)
- }
- }
- if len(filtered) == 0 {
- return nil, fmt.Errorf("no object %q declared in file %s",
- spec.fromName, spec.filename)
- } else if len(filtered) > 1 {
- return nil, ambiguityError(iprog.Fset, filtered)
- }
- fromObjects = append(fromObjects, filtered[0])
- }
- break
- }
- }
- if len(fromObjects) == 0 {
- // can't happen?
- return nil, fmt.Errorf("file %s was not part of the loaded program", spec.filename)
- }
- return fromObjects, nil
-}
-
-func typeSwitchVar(info *types.Info, path []ast.Node) types.Object {
- if len(path) > 3 {
- // [Ident AssignStmt TypeSwitchStmt...]
- if sw, ok := path[2].(*ast.TypeSwitchStmt); ok {
- // choose the first case.
- if len(sw.Body.List) > 0 {
- obj := info.Implicits[sw.Body.List[0].(*ast.CaseClause)]
- if obj != nil {
- return obj
- }
- }
- }
- }
- return nil
-}
-
-// On success, findObjects returns the list of objects named
-// spec.fromName matching the spec. On success, the result has exactly
-// one element unless spec.searchFor!="", in which case it has at least one
-// element.
-//
-func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
- if spec.pkgMember == "" {
- if spec.searchFor == "" {
- panic(spec)
- }
- objects := searchDefs(&info.Info, spec.searchFor)
- if objects == nil {
- return nil, fmt.Errorf("no object %q declared in package %q",
- spec.searchFor, info.Pkg.Path())
- }
- return objects, nil
- }
-
- pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember)
- if pkgMember == nil {
- return nil, fmt.Errorf("package %q has no member %q",
- info.Pkg.Path(), spec.pkgMember)
- }
-
- var searchFunc *types.Func
- if spec.typeMember == "" {
- // package member
- if spec.searchFor == "" {
- return []types.Object{pkgMember}, nil
- }
-
- // Search within pkgMember, which must be a function.
- searchFunc, _ = pkgMember.(*types.Func)
- if searchFunc == nil {
- return nil, fmt.Errorf("cannot search for %q within %s %q",
- spec.searchFor, objectKind(pkgMember), pkgMember)
- }
- } else {
- // field/method of type
- // e.g. (encoding/json.Decoder).Decode
- // or ::x within it.
-
- tName, _ := pkgMember.(*types.TypeName)
- if tName == nil {
- return nil, fmt.Errorf("%s.%s is a %s, not a type",
- info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember))
- }
-
- // search within named type.
- obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember)
- if obj == nil {
- return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s",
- spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name())
- }
-
- if spec.searchFor == "" {
- // If it is an embedded field, return the type of the field.
- if v, ok := obj.(*types.Var); ok && v.Anonymous() {
- switch t := v.Type().(type) {
- case *types.Pointer:
- return []types.Object{t.Elem().(*types.Named).Obj()}, nil
- case *types.Named:
- return []types.Object{t.Obj()}, nil
- }
- }
- return []types.Object{obj}, nil
- }
-
- searchFunc, _ = obj.(*types.Func)
- if searchFunc == nil {
- return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function",
- spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(),
- obj.Name())
- }
- if isInterface(tName.Type()) {
- return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s",
- spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name())
- }
- }
-
- // -- search within function or method --
-
- decl := funcDecl(info, searchFunc)
- if decl == nil {
- return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen?
- }
-
- var objects []types.Object
- for _, obj := range searchDefs(&info.Info, spec.searchFor) {
- // We use positions, not scopes, to determine whether
- // the obj is within searchFunc. This is clumsy, but the
- // alternative, using the types.Scope tree, doesn't
- // account for non-lexical objects like fields and
- // interface methods.
- if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc {
- objects = append(objects, obj)
- }
- }
- if objects == nil {
- return nil, fmt.Errorf("no local definition of %q within %s",
- spec.searchFor, searchFunc)
- }
- return objects, nil
-}
-
-func funcDecl(info *loader.PackageInfo, fn *types.Func) *ast.FuncDecl {
- for _, f := range info.Files {
- for _, d := range f.Decls {
- if d, ok := d.(*ast.FuncDecl); ok && info.Defs[d.Name] == fn {
- return d
- }
- }
- }
- return nil
-}
-
-func searchDefs(info *types.Info, name string) []types.Object {
- var objects []types.Object
- for id, obj := range info.Defs {
- if obj == nil {
- // e.g. blank ident.
- // TODO(adonovan): but also implicit y in
- // switch y := x.(type)
- // Needs some thought.
- continue
- }
- if id.Name == name {
- objects = append(objects, obj)
- }
- }
- return objects
-}
-
-func identAtOffset(fset *token.FileSet, f *ast.File, offset int) *ast.Ident {
- var found *ast.Ident
- ast.Inspect(f, func(n ast.Node) bool {
- if id, ok := n.(*ast.Ident); ok {
- idpos := fset.Position(id.Pos()).Offset
- if idpos <= offset && offset < idpos+len(id.Name) {
- found = id
- }
- }
- return found == nil // keep traversing only until found
- })
- return found
-}
-
-// ambiguityError returns an error describing an ambiguous "*" scope query.
-func ambiguityError(fset *token.FileSet, objects []types.Object) error {
- var buf bytes.Buffer
- for i, obj := range objects {
- if i > 0 {
- buf.WriteString(", ")
- }
- posn := fset.Position(obj.Pos())
- fmt.Fprintf(&buf, "%s at %s:%d",
- objectKind(obj), filepath.Base(posn.Filename), posn.Column)
- }
- return fmt.Errorf("ambiguous specifier %s matches %s",
- objects[0].Name(), buf.String())
-}
diff --git a/refactor/rename/util14.go b/refactor/rename/util14.go
deleted file mode 100644
index 126d3eec13..0000000000
--- a/refactor/rename/util14.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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 rename
-
-import (
- "go/ast"
- "os"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
- "unicode"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
-)
-
-func objectKind(obj types.Object) string {
- switch obj := obj.(type) {
- case *types.PkgName:
- return "imported package name"
- case *types.TypeName:
- return "type"
- case *types.Var:
- if obj.IsField() {
- return "field"
- }
- case *types.Func:
- if obj.Type().(*types.Signature).Recv() != nil {
- return "method"
- }
- }
- // label, func, var, const
- return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
-}
-
-func typeKind(T types.Type) string {
- return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(T.Underlying()).String(), "*types."))
-}
-
-// NB: for renamings, blank is not considered valid.
-func isValidIdentifier(id string) bool {
- if id == "" || id == "_" {
- return false
- }
- for i, r := range id {
- if !isLetter(r) && (i == 0 || !isDigit(r)) {
- return false
- }
- }
- return true
-}
-
-// isLocal reports whether obj is local to some function.
-// Precondition: not a struct field or interface method.
-func isLocal(obj types.Object) bool {
- // [... 5=stmt 4=func 3=file 2=pkg 1=universe]
- var depth int
- for scope := obj.Parent(); scope != nil; scope = scope.Parent() {
- depth++
- }
- return depth >= 4
-}
-
-func isPackageLevel(obj types.Object) bool {
- return obj.Pkg().Scope().Lookup(obj.Name()) == obj
-}
-
-// -- Plundered from go/scanner: ---------------------------------------
-
-func isLetter(ch rune) bool {
- return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
-}
-
-func isDigit(ch rune) bool {
- return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
-}
-
-// -- Plundered from golang.org/x/tools/oracle -----------------
-
-// sameFile returns true if x and y have the same basename and denote
-// the same file.
-//
-func sameFile(x, y string) bool {
- if runtime.GOOS == "windows" {
- x = filepath.ToSlash(x)
- y = filepath.ToSlash(y)
- }
- if x == y {
- return true
- }
- if filepath.Base(x) == filepath.Base(y) { // (optimisation)
- if xi, err := os.Stat(x); err == nil {
- if yi, err := os.Stat(y); err == nil {
- return os.SameFile(xi, yi)
- }
- }
- }
- return false
-}
-
-func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
diff --git a/refactor/satisfy/find14.go b/refactor/satisfy/find14.go
deleted file mode 100644
index 3b8d1e096a..0000000000
--- a/refactor/satisfy/find14.go
+++ /dev/null
@@ -1,707 +0,0 @@
-// 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 satisfy inspects the type-checked ASTs of Go packages and
-// reports the set of discovered type constraints of the form (lhs, rhs
-// Type) where lhs is a non-trivial interface, rhs satisfies this
-// interface, and this fact is necessary for the package to be
-// well-typed.
-//
-// THIS PACKAGE IS EXPERIMENTAL AND MAY CHANGE AT ANY TIME.
-//
-// It is provided only for the gorename tool. Ideally this
-// functionality will become part of the type-checker in due course,
-// since it is computing it anyway, and it is robust for ill-typed
-// inputs, which this package is not.
-//
-package satisfy // import "golang.org/x/tools/refactor/satisfy"
-
-// NOTES:
-//
-// We don't care about numeric conversions, so we don't descend into
-// types or constant expressions. This is unsound because
-// constant expressions can contain arbitrary statements, e.g.
-// const x = len([1]func(){func() {
-// ...
-// }})
-//
-// TODO(adonovan): make this robust against ill-typed input.
-// Or move it into the type-checker.
-//
-// Assignability conversions are possible in the following places:
-// - in assignments y = x, y := x, var y = x.
-// - from call argument types to formal parameter types
-// - in append and delete calls
-// - from return operands to result parameter types
-// - in composite literal T{k:v}, from k and v to T's field/element/key type
-// - in map[key] from key to the map's key type
-// - in comparisons x==y and switch x { case y: }.
-// - in explicit conversions T(x)
-// - in sends ch <- x, from x to the channel element type
-// - in type assertions x.(T) and switch x.(type) { case T: }
-//
-// The results of this pass provide information equivalent to the
-// ssa.MakeInterface and ssa.ChangeInterface instructions.
-
-import (
- "fmt"
- "go/ast"
- "go/token"
-
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
- "golang.org/x/tools/go/types/typeutil"
-)
-
-// A Constraint records the fact that the RHS type does and must
-// satisify the LHS type, which is an interface.
-// The names are suggestive of an assignment statement LHS = RHS.
-type Constraint struct {
- LHS, RHS types.Type
-}
-
-// A Finder inspects the type-checked ASTs of Go packages and
-// accumulates the set of type constraints (x, y) such that x is
-// assignable to y, y is an interface, and both x and y have methods.
-//
-// In other words, it returns the subset of the "implements" relation
-// that is checked during compilation of a package. Refactoring tools
-// will need to preserve at least this part of the relation to ensure
-// continued compilation.
-//
-type Finder struct {
- Result map[Constraint]bool
- msetcache typeutil.MethodSetCache
-
- // per-Find state
- info *types.Info
- sig *types.Signature
-}
-
-// Find inspects a single package, populating Result with its pairs of
-// constrained types.
-//
-// The result is non-canonical and thus may contain duplicates (but this
-// tends to preserves names of interface types better).
-//
-// The package must be free of type errors, and
-// info.{Defs,Uses,Selections,Types} must have been populated by the
-// type-checker.
-//
-func (f *Finder) Find(info *types.Info, files []*ast.File) {
- if f.Result == nil {
- f.Result = make(map[Constraint]bool)
- }
-
- f.info = info
- for _, file := range files {
- for _, d := range file.Decls {
- switch d := d.(type) {
- case *ast.GenDecl:
- if d.Tok == token.VAR { // ignore consts
- for _, spec := range d.Specs {
- f.valueSpec(spec.(*ast.ValueSpec))
- }
- }
-
- case *ast.FuncDecl:
- if d.Body != nil {
- f.sig = f.info.Defs[d.Name].Type().(*types.Signature)
- f.stmt(d.Body)
- f.sig = nil
- }
- }
- }
- }
- f.info = nil
-}
-
-var (
- tInvalid = types.Typ[types.Invalid]
- tUntypedBool = types.Typ[types.UntypedBool]
- tUntypedNil = types.Typ[types.UntypedNil]
-)
-
-// exprN visits an expression in a multi-value context.
-func (f *Finder) exprN(e ast.Expr) types.Type {
- typ := f.info.Types[e].Type.(*types.Tuple)
- switch e := e.(type) {
- case *ast.ParenExpr:
- return f.exprN(e.X)
-
- case *ast.CallExpr:
- // x, err := f(args)
- sig := f.expr(e.Fun).Underlying().(*types.Signature)
- f.call(sig, e.Args)
-
- case *ast.IndexExpr:
- // y, ok := x[i]
- x := f.expr(e.X)
- f.assign(f.expr(e.Index), x.Underlying().(*types.Map).Key())
-
- case *ast.TypeAssertExpr:
- // y, ok := x.(T)
- f.typeAssert(f.expr(e.X), typ.At(0).Type())
-
- case *ast.UnaryExpr: // must be receive <-
- // y, ok := <-x
- f.expr(e.X)
-
- default:
- panic(e)
- }
- return typ
-}
-
-func (f *Finder) call(sig *types.Signature, args []ast.Expr) {
- if len(args) == 0 {
- return
- }
-
- // Ellipsis call? e.g. f(x, y, z...)
- if _, ok := args[len(args)-1].(*ast.Ellipsis); ok {
- for i, arg := range args {
- // The final arg is a slice, and so is the final param.
- f.assign(sig.Params().At(i).Type(), f.expr(arg))
- }
- return
- }
-
- var argtypes []types.Type
-
- // Gather the effective actual parameter types.
- if tuple, ok := f.info.Types[args[0]].Type.(*types.Tuple); ok {
- // f(g()) call where g has multiple results?
- f.expr(args[0])
- // unpack the tuple
- for i := 0; i < tuple.Len(); i++ {
- argtypes = append(argtypes, tuple.At(i).Type())
- }
- } else {
- for _, arg := range args {
- argtypes = append(argtypes, f.expr(arg))
- }
- }
-
- // Assign the actuals to the formals.
- if !sig.Variadic() {
- for i, argtype := range argtypes {
- f.assign(sig.Params().At(i).Type(), argtype)
- }
- } else {
- // The first n-1 parameters are assigned normally.
- nnormals := sig.Params().Len() - 1
- for i, argtype := range argtypes[:nnormals] {
- f.assign(sig.Params().At(i).Type(), argtype)
- }
- // Remaining args are assigned to elements of varargs slice.
- tElem := sig.Params().At(nnormals).Type().(*types.Slice).Elem()
- for i := nnormals; i < len(argtypes); i++ {
- f.assign(tElem, argtypes[i])
- }
- }
-}
-
-func (f *Finder) builtin(obj *types.Builtin, sig *types.Signature, args []ast.Expr, T types.Type) types.Type {
- switch obj.Name() {
- case "make", "new":
- // skip the type operand
- for _, arg := range args[1:] {
- f.expr(arg)
- }
-
- case "append":
- s := f.expr(args[0])
- if _, ok := args[len(args)-1].(*ast.Ellipsis); ok && len(args) == 2 {
- // append(x, y...) including append([]byte, "foo"...)
- f.expr(args[1])
- } else {
- // append(x, y, z)
- tElem := s.Underlying().(*types.Slice).Elem()
- for _, arg := range args[1:] {
- f.assign(tElem, f.expr(arg))
- }
- }
-
- case "delete":
- m := f.expr(args[0])
- k := f.expr(args[1])
- f.assign(m.Underlying().(*types.Map).Key(), k)
-
- default:
- // ordinary call
- f.call(sig, args)
- }
-
- return T
-}
-
-func (f *Finder) extract(tuple types.Type, i int) types.Type {
- if tuple, ok := tuple.(*types.Tuple); ok && i < tuple.Len() {
- return tuple.At(i).Type()
- }
- return tInvalid
-}
-
-func (f *Finder) valueSpec(spec *ast.ValueSpec) {
- var T types.Type
- if spec.Type != nil {
- T = f.info.Types[spec.Type].Type
- }
- switch len(spec.Values) {
- case len(spec.Names): // e.g. var x, y = f(), g()
- for _, value := range spec.Values {
- v := f.expr(value)
- if T != nil {
- f.assign(T, v)
- }
- }
-
- case 1: // e.g. var x, y = f()
- tuple := f.exprN(spec.Values[0])
- for i := range spec.Names {
- if T != nil {
- f.assign(T, f.extract(tuple, i))
- }
- }
- }
-}
-
-// assign records pairs of distinct types that are related by
-// assignability, where the left-hand side is an interface and both
-// sides have methods.
-//
-// It should be called for all assignability checks, type assertions,
-// explicit conversions and comparisons between two types, unless the
-// types are uninteresting (e.g. lhs is a concrete type, or the empty
-// interface; rhs has no methods).
-//
-func (f *Finder) assign(lhs, rhs types.Type) {
- if types.Identical(lhs, rhs) {
- return
- }
- if !isInterface(lhs) {
- return
- }
-
- if f.msetcache.MethodSet(lhs).Len() == 0 {
- return
- }
- if f.msetcache.MethodSet(rhs).Len() == 0 {
- return
- }
- // record the pair
- f.Result[Constraint{lhs, rhs}] = true
-}
-
-// typeAssert must be called for each type assertion x.(T) where x has
-// interface type I.
-func (f *Finder) typeAssert(I, T types.Type) {
- // Type assertions are slightly subtle, because they are allowed
- // to be "impossible", e.g.
- //
- // var x interface{f()}
- // _ = x.(interface{f()int}) // legal
- //
- // (In hindsight, the language spec should probably not have
- // allowed this, but it's too late to fix now.)
- //
- // This means that a type assert from I to T isn't exactly a
- // constraint that T is assignable to I, but for a refactoring
- // tool it is a conditional constraint that, if T is assignable
- // to I before a refactoring, it should remain so after.
-
- if types.AssignableTo(T, I) {
- f.assign(I, T)
- }
-}
-
-// compare must be called for each comparison x==y.
-func (f *Finder) compare(x, y types.Type) {
- if types.AssignableTo(x, y) {
- f.assign(y, x)
- } else if types.AssignableTo(y, x) {
- f.assign(x, y)
- }
-}
-
-// expr visits a true expression (not a type or defining ident)
-// and returns its type.
-func (f *Finder) expr(e ast.Expr) types.Type {
- tv := f.info.Types[e]
- if tv.Value != nil {
- return tv.Type // prune the descent for constants
- }
-
- // tv.Type may be nil for an ast.Ident.
-
- switch e := e.(type) {
- case *ast.BadExpr, *ast.BasicLit:
- // no-op
-
- case *ast.Ident:
- // (referring idents only)
- if obj, ok := f.info.Uses[e]; ok {
- return obj.Type()
- }
- if e.Name == "_" { // e.g. "for _ = range x"
- return tInvalid
- }
- panic("undefined ident: " + e.Name)
-
- case *ast.Ellipsis:
- if e.Elt != nil {
- f.expr(e.Elt)
- }
-
- case *ast.FuncLit:
- saved := f.sig
- f.sig = tv.Type.(*types.Signature)
- f.stmt(e.Body)
- f.sig = saved
-
- case *ast.CompositeLit:
- switch T := deref(tv.Type).Underlying().(type) {
- case *types.Struct:
- for i, elem := range e.Elts {
- if kv, ok := elem.(*ast.KeyValueExpr); ok {
- f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
- } else {
- f.assign(T.Field(i).Type(), f.expr(elem))
- }
- }
-
- case *types.Map:
- for _, elem := range e.Elts {
- elem := elem.(*ast.KeyValueExpr)
- f.assign(T.Key(), f.expr(elem.Key))
- f.assign(T.Elem(), f.expr(elem.Value))
- }
-
- case *types.Array, *types.Slice:
- tElem := T.(interface {
- Elem() types.Type
- }).Elem()
- for _, elem := range e.Elts {
- if kv, ok := elem.(*ast.KeyValueExpr); ok {
- // ignore the key
- f.assign(tElem, f.expr(kv.Value))
- } else {
- f.assign(tElem, f.expr(elem))
- }
- }
-
- default:
- panic("unexpected composite literal type: " + tv.Type.String())
- }
-
- case *ast.ParenExpr:
- f.expr(e.X)
-
- case *ast.SelectorExpr:
- if _, ok := f.info.Selections[e]; ok {
- f.expr(e.X) // selection
- } else {
- return f.info.Uses[e.Sel].Type() // qualified identifier
- }
-
- case *ast.IndexExpr:
- x := f.expr(e.X)
- i := f.expr(e.Index)
- if ux, ok := x.Underlying().(*types.Map); ok {
- f.assign(ux.Key(), i)
- }
-
- case *ast.SliceExpr:
- f.expr(e.X)
- if e.Low != nil {
- f.expr(e.Low)
- }
- if e.High != nil {
- f.expr(e.High)
- }
- if e.Max != nil {
- f.expr(e.Max)
- }
-
- case *ast.TypeAssertExpr:
- x := f.expr(e.X)
- f.typeAssert(x, f.info.Types[e.Type].Type)
-
- case *ast.CallExpr:
- if tvFun := f.info.Types[e.Fun]; tvFun.IsType() {
- // conversion
- arg0 := f.expr(e.Args[0])
- f.assign(tvFun.Type, arg0)
- } else {
- // function call
- if id, ok := unparen(e.Fun).(*ast.Ident); ok {
- if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
- sig := f.info.Types[id].Type.(*types.Signature)
- return f.builtin(obj, sig, e.Args, tv.Type)
- }
- }
- // ordinary call
- f.call(f.expr(e.Fun).Underlying().(*types.Signature), e.Args)
- }
-
- case *ast.StarExpr:
- f.expr(e.X)
-
- case *ast.UnaryExpr:
- f.expr(e.X)
-
- case *ast.BinaryExpr:
- x := f.expr(e.X)
- y := f.expr(e.Y)
- if e.Op == token.EQL || e.Op == token.NEQ {
- f.compare(x, y)
- }
-
- case *ast.KeyValueExpr:
- f.expr(e.Key)
- f.expr(e.Value)
-
- case *ast.ArrayType,
- *ast.StructType,
- *ast.FuncType,
- *ast.InterfaceType,
- *ast.MapType,
- *ast.ChanType:
- panic(e)
- }
-
- if tv.Type == nil {
- panic(fmt.Sprintf("no type for %T", e))
- }
-
- return tv.Type
-}
-
-func (f *Finder) stmt(s ast.Stmt) {
- switch s := s.(type) {
- case *ast.BadStmt,
- *ast.EmptyStmt,
- *ast.BranchStmt:
- // no-op
-
- case *ast.DeclStmt:
- d := s.Decl.(*ast.GenDecl)
- if d.Tok == token.VAR { // ignore consts
- for _, spec := range d.Specs {
- f.valueSpec(spec.(*ast.ValueSpec))
- }
- }
-
- case *ast.LabeledStmt:
- f.stmt(s.Stmt)
-
- case *ast.ExprStmt:
- f.expr(s.X)
-
- case *ast.SendStmt:
- ch := f.expr(s.Chan)
- val := f.expr(s.Value)
- f.assign(ch.Underlying().(*types.Chan).Elem(), val)
-
- case *ast.IncDecStmt:
- f.expr(s.X)
-
- case *ast.AssignStmt:
- switch s.Tok {
- case token.ASSIGN, token.DEFINE:
- // y := x or y = x
- var rhsTuple types.Type
- if len(s.Lhs) != len(s.Rhs) {
- rhsTuple = f.exprN(s.Rhs[0])
- }
- for i := range s.Lhs {
- var lhs, rhs types.Type
- if rhsTuple == nil {
- rhs = f.expr(s.Rhs[i]) // 1:1 assignment
- } else {
- rhs = f.extract(rhsTuple, i) // n:1 assignment
- }
-
- if id, ok := s.Lhs[i].(*ast.Ident); ok {
- if id.Name != "_" {
- if obj, ok := f.info.Defs[id]; ok {
- lhs = obj.Type() // definition
- }
- }
- }
- if lhs == nil {
- lhs = f.expr(s.Lhs[i]) // assignment
- }
- f.assign(lhs, rhs)
- }
-
- default:
- // y op= x
- f.expr(s.Lhs[0])
- f.expr(s.Rhs[0])
- }
-
- case *ast.GoStmt:
- f.expr(s.Call)
-
- case *ast.DeferStmt:
- f.expr(s.Call)
-
- case *ast.ReturnStmt:
- formals := f.sig.Results()
- switch len(s.Results) {
- case formals.Len(): // 1:1
- for i, result := range s.Results {
- f.assign(formals.At(i).Type(), f.expr(result))
- }
-
- case 1: // n:1
- tuple := f.exprN(s.Results[0])
- for i := 0; i < formals.Len(); i++ {
- f.assign(formals.At(i).Type(), f.extract(tuple, i))
- }
- }
-
- case *ast.SelectStmt:
- f.stmt(s.Body)
-
- case *ast.BlockStmt:
- for _, s := range s.List {
- f.stmt(s)
- }
-
- case *ast.IfStmt:
- if s.Init != nil {
- f.stmt(s.Init)
- }
- f.expr(s.Cond)
- f.stmt(s.Body)
- if s.Else != nil {
- f.stmt(s.Else)
- }
-
- case *ast.SwitchStmt:
- if s.Init != nil {
- f.stmt(s.Init)
- }
- var tag types.Type = tUntypedBool
- if s.Tag != nil {
- tag = f.expr(s.Tag)
- }
- for _, cc := range s.Body.List {
- cc := cc.(*ast.CaseClause)
- for _, cond := range cc.List {
- f.compare(tag, f.info.Types[cond].Type)
- }
- for _, s := range cc.Body {
- f.stmt(s)
- }
- }
-
- case *ast.TypeSwitchStmt:
- if s.Init != nil {
- f.stmt(s.Init)
- }
- var I types.Type
- switch ass := s.Assign.(type) {
- case *ast.ExprStmt: // x.(type)
- I = f.expr(unparen(ass.X).(*ast.TypeAssertExpr).X)
- case *ast.AssignStmt: // y := x.(type)
- I = f.expr(unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
- }
- for _, cc := range s.Body.List {
- cc := cc.(*ast.CaseClause)
- for _, cond := range cc.List {
- tCase := f.info.Types[cond].Type
- if tCase != tUntypedNil {
- f.typeAssert(I, tCase)
- }
- }
- for _, s := range cc.Body {
- f.stmt(s)
- }
- }
-
- case *ast.CommClause:
- if s.Comm != nil {
- f.stmt(s.Comm)
- }
- for _, s := range s.Body {
- f.stmt(s)
- }
-
- case *ast.ForStmt:
- if s.Init != nil {
- f.stmt(s.Init)
- }
- if s.Cond != nil {
- f.expr(s.Cond)
- }
- if s.Post != nil {
- f.stmt(s.Post)
- }
- f.stmt(s.Body)
-
- case *ast.RangeStmt:
- x := f.expr(s.X)
- // No conversions are involved when Tok==DEFINE.
- if s.Tok == token.ASSIGN {
- if s.Key != nil {
- k := f.expr(s.Key)
- var xelem types.Type
- // keys of array, *array, slice, string aren't interesting
- switch ux := x.Underlying().(type) {
- case *types.Chan:
- xelem = ux.Elem()
- case *types.Map:
- xelem = ux.Key()
- }
- if xelem != nil {
- f.assign(xelem, k)
- }
- }
- if s.Value != nil {
- val := f.expr(s.Value)
- var xelem types.Type
- // values of strings aren't interesting
- switch ux := x.Underlying().(type) {
- case *types.Array:
- xelem = ux.Elem()
- case *types.Chan:
- xelem = ux.Elem()
- case *types.Map:
- xelem = ux.Elem()
- case *types.Pointer: // *array
- xelem = deref(ux).(*types.Array).Elem()
- case *types.Slice:
- xelem = ux.Elem()
- }
- if xelem != nil {
- f.assign(xelem, val)
- }
- }
- }
- f.stmt(s.Body)
-
- default:
- panic(s)
- }
-}
-
-// -- Plundered from golang.org/x/tools/go/ssa -----------------
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
- if p, ok := typ.Underlying().(*types.Pointer); ok {
- return p.Elem()
- }
- return typ
-}
-
-func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }