mirror of
https://github.com/golang/go
synced 2024-11-19 01:04:40 -07:00
997b3545fd
The optional Qualifier function determines what prefix to attach to package-level names, enabling clients to qualify packages in different ways, for example, using only the package name instead of its complete path, or using the locally appropriate name for package given a set of (possibly renaming) imports. Prior to this change, clients wanting this behavior had to copy hundreds of lines of complex printing logic. Fun fact: (*types.Package).Path and (*types.Package).Name are valid Qualifier functions. We provide the RelativeTo helper function to create Qualifiers so that the old behavior remains a one-liner. Fixes golang/go#11133 Change-Id: Ibd63f639c7b3aa1738826d6165f2d810efeb8293 Reviewed-on: https://go-review.googlesource.com/11692 Reviewed-by: Robert Griesemer <gri@golang.org>
764 lines
18 KiB
Go
764 lines
18 KiB
Go
// Package lexical computes the structure of the lexical environment,
|
|
// including the definition of and references to all universal,
|
|
// package-level, file-level and function-local entities. It does not
|
|
// record qualified identifiers, labels, struct fields, or methods.
|
|
//
|
|
// It is intended for renaming and refactoring tools, which need a more
|
|
// precise understanding of identifier resolution than is available from
|
|
// the output of the type-checker alone.
|
|
//
|
|
// THIS INTERFACE IS EXPERIMENTAL AND MAY CHANGE OR BE REMOVED IN FUTURE.
|
|
//
|
|
package lexical // import "golang.org/x/tools/refactor/lexical"
|
|
|
|
// OVERVIEW
|
|
//
|
|
// As we traverse the AST, we build a "spaghetti stack" of Blocks,
|
|
// i.e. a tree with parent edges pointing to the root. Each time we
|
|
// visit an identifier that's a reference into the lexical environment,
|
|
// we create and save an Environment, which captures the current mapping
|
|
// state of the Block; these are saved for the client.
|
|
//
|
|
// We don't bother recording non-lexical references.
|
|
|
|
// TODO(adonovan):
|
|
// - make it robust against syntax errors. Audit all type assertions, etc.
|
|
// - better still, after the Go 1.4 thaw, move this into go/types.
|
|
// I don't think it need be a big change since the visitor is already there;
|
|
// we just need to records Environments. lexical.Block is analogous
|
|
// to types.Scope.
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/token"
|
|
"os"
|
|
"strconv"
|
|
|
|
"golang.org/x/tools/go/types"
|
|
)
|
|
|
|
const trace = false
|
|
|
|
var logf = func(format string, args ...interface{}) {
|
|
fmt.Fprintf(os.Stderr, format, args...)
|
|
}
|
|
|
|
// A Block is a level of the lexical environment, a tree of blocks.
|
|
// It maps names to objects.
|
|
//
|
|
type Block struct {
|
|
kind string // one of universe package file func block if switch typeswitch case for range
|
|
syntax ast.Node // syntax declaring the block (nil for universe and package) [needed?]
|
|
|
|
parent Environment
|
|
bindings []types.Object // bindings in lexical order
|
|
index map[string]int // maps a name to the index of its binding, for fast lookup
|
|
}
|
|
|
|
// An Environment is a snapshot of a Block taken at a certain lexical
|
|
// position. It may contain bindings for fewer names than the
|
|
// (completed) block, or different bindings for names that are
|
|
// re-defined later in the block.
|
|
//
|
|
// For example, the lexical Block for the function f below contains a
|
|
// binding for the local var x, but the Environments captured by at the
|
|
// two print(x) calls differ: the first contains this binding, the
|
|
// second does not. The first Environment contains a different binding
|
|
// for x: the string var defined in the package block, an ancestor.
|
|
//
|
|
// var x string
|
|
// func f() {
|
|
// print(x)
|
|
// x := 1
|
|
// print(x)
|
|
// }
|
|
//
|
|
type Environment struct {
|
|
block *Block
|
|
nbindings int // length of prefix of block.bindings that's visible
|
|
}
|
|
|
|
// Depth returns the depth of this block in the block tree.
|
|
// The universal block has depth 1, a package block 2, a file block 3, etc.
|
|
func (b *Block) Depth() int {
|
|
if b == nil {
|
|
return 0
|
|
}
|
|
return 1 + b.parent.block.Depth()
|
|
}
|
|
|
|
// env returns an Environment that is a snapshot of b's current state.
|
|
func (b *Block) env() Environment {
|
|
return Environment{b, len(b.bindings)}
|
|
}
|
|
|
|
// Lookup returns the definition of name in the environment specified by
|
|
// env, and the Block that defines it, which may be an ancestor.
|
|
func (env Environment) Lookup(name string) (types.Object, *Block) {
|
|
if env.block == nil {
|
|
return nil, nil
|
|
}
|
|
return lookup(env.block, name, env.nbindings)
|
|
}
|
|
|
|
// nbindings specifies what prefix of b.bindings should be considered visible.
|
|
func lookup(b *Block, name string, nbindings int) (types.Object, *Block) {
|
|
if b == nil {
|
|
return nil, nil
|
|
}
|
|
if i, ok := b.index[name]; ok && i < nbindings {
|
|
return b.bindings[i], b
|
|
}
|
|
|
|
parent := b.parent
|
|
if parent.block == nil {
|
|
return nil, nil
|
|
}
|
|
return lookup(parent.block, name, parent.nbindings)
|
|
}
|
|
|
|
// Lookup returns the definition of name in the environment specified by
|
|
// b, and the Block that defines it, which may be an ancestor.
|
|
func (b *Block) Lookup(name string) (types.Object, *Block) {
|
|
return b.env().Lookup(name)
|
|
}
|
|
|
|
// Block returns the block of which this environment is a partial view.
|
|
func (env Environment) Block() *Block {
|
|
return env.block
|
|
}
|
|
|
|
func (env Environment) String() string {
|
|
return fmt.Sprintf("%s:%d", env.block, env.nbindings)
|
|
}
|
|
|
|
func (b *Block) String() string {
|
|
var s string
|
|
if b.parent.block != nil {
|
|
s = b.parent.block.String()
|
|
s += "."
|
|
}
|
|
return s + b.kind
|
|
}
|
|
|
|
var universe = &Block{kind: "universe", index: make(map[string]int)}
|
|
|
|
func init() {
|
|
for i, name := range types.Universe.Names() {
|
|
obj := types.Universe.Lookup(name)
|
|
universe.bindings = append(universe.bindings, obj)
|
|
universe.index[name] = i
|
|
}
|
|
}
|
|
|
|
// -- resolver ---------------------------------------------------------
|
|
|
|
// A Reference provides the lexical environment for a given reference to
|
|
// an object in lexical scope.
|
|
type Reference struct {
|
|
Id *ast.Ident
|
|
Env Environment
|
|
}
|
|
|
|
// resolver holds the state of the identifier resolution visitation:
|
|
// the package information, the result, and the current block.
|
|
type resolver struct {
|
|
fset *token.FileSet
|
|
imports map[string]*types.Package
|
|
pkg *types.Package
|
|
info *types.Info
|
|
|
|
// visitor state
|
|
block *Block
|
|
|
|
result *Info
|
|
}
|
|
|
|
func (r *resolver) setBlock(kind string, syntax ast.Node) *Block {
|
|
b := &Block{
|
|
kind: kind,
|
|
syntax: syntax,
|
|
parent: r.block.env(),
|
|
index: make(map[string]int),
|
|
}
|
|
if syntax != nil {
|
|
r.result.Blocks[syntax] = b
|
|
}
|
|
r.block = b
|
|
return b
|
|
}
|
|
|
|
func (r *resolver) qualifier(pkg *types.Package) string {
|
|
if pkg == r.pkg {
|
|
return "" // unqualified intra-package reference
|
|
}
|
|
return pkg.Path()
|
|
}
|
|
|
|
func (r *resolver) use(id *ast.Ident, env Environment) {
|
|
if id.Name == "_" {
|
|
return // an error
|
|
}
|
|
obj, _ := env.Lookup(id.Name)
|
|
if obj == nil {
|
|
logf("%s: lookup of %s failed\n", r.fset.Position(id.Pos()), id.Name)
|
|
} else if want := r.info.Uses[id]; obj != want {
|
|
// sanity check against go/types resolver
|
|
logf("%s: internal error: lookup of %s yielded wrong object: got %v (%s), want %v\n",
|
|
r.fset.Position(id.Pos()), id.Name, types.ObjectString(obj, r.qualifier),
|
|
r.fset.Position(obj.Pos()),
|
|
want)
|
|
}
|
|
if trace {
|
|
logf("use %s = %v in %s\n", id.Name, types.ObjectString(obj, r.qualifier), env)
|
|
}
|
|
|
|
r.result.Refs[obj] = append(r.result.Refs[obj], Reference{id, env})
|
|
}
|
|
|
|
func (r *resolver) define(b *Block, id *ast.Ident) {
|
|
obj := r.info.Defs[id]
|
|
if obj == nil {
|
|
logf("%s: internal error: not a defining ident: %s\n",
|
|
r.fset.Position(id.Pos()), id.Name)
|
|
panic(id)
|
|
}
|
|
r.defineObject(b, id.Name, obj)
|
|
|
|
// Objects (other than PkgName) defined at file scope
|
|
// are also defined in the enclosing package scope.
|
|
if _, ok := b.syntax.(*ast.File); ok {
|
|
switch obj.(type) {
|
|
default:
|
|
r.defineObject(b.parent.block, id.Name, obj)
|
|
case nil, *types.PkgName:
|
|
}
|
|
}
|
|
}
|
|
|
|
// Used for implicit objects created by some ImportSpecs and CaseClauses.
|
|
func (r *resolver) defineImplicit(b *Block, n ast.Node, name string) {
|
|
obj := r.info.Implicits[n]
|
|
if obj == nil {
|
|
logf("%s: internal error: not an implicit definition: %T\n",
|
|
r.fset.Position(n.Pos()), n)
|
|
}
|
|
r.defineObject(b, name, obj)
|
|
}
|
|
|
|
func (r *resolver) defineObject(b *Block, name string, obj types.Object) {
|
|
if obj.Name() == "_" {
|
|
return
|
|
}
|
|
i := len(b.bindings)
|
|
b.bindings = append(b.bindings, obj)
|
|
b.index[name] = i
|
|
if trace {
|
|
logf("def %s = %s in %s\n", name, types.ObjectString(obj, r.qualifier), b)
|
|
}
|
|
r.result.Defs[obj] = b
|
|
}
|
|
|
|
func (r *resolver) function(recv *ast.FieldList, typ *ast.FuncType, body *ast.BlockStmt, syntax ast.Node) {
|
|
// Use all signature types in enclosing block.
|
|
r.expr(typ)
|
|
r.fieldList(recv, false)
|
|
|
|
savedBlock := r.block // save
|
|
r.setBlock("func", syntax)
|
|
|
|
// Define all parameters/results, and visit the body, within the func block.
|
|
r.fieldList(typ.Params, true)
|
|
r.fieldList(typ.Results, true)
|
|
r.fieldList(recv, true)
|
|
if body != nil {
|
|
r.stmtList(body.List)
|
|
}
|
|
|
|
r.block = savedBlock // restore
|
|
}
|
|
|
|
func (r *resolver) fieldList(list *ast.FieldList, def bool) {
|
|
if list != nil {
|
|
for _, f := range list.List {
|
|
if def {
|
|
for _, id := range f.Names {
|
|
r.define(r.block, id)
|
|
}
|
|
} else {
|
|
r.expr(f.Type)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *resolver) exprList(list []ast.Expr) {
|
|
for _, x := range list {
|
|
r.expr(x)
|
|
}
|
|
}
|
|
|
|
func (r *resolver) expr(n ast.Expr) {
|
|
switch n := n.(type) {
|
|
case *ast.BadExpr:
|
|
case *ast.BasicLit:
|
|
// no-op
|
|
|
|
case *ast.Ident:
|
|
r.use(n, r.block.env())
|
|
|
|
case *ast.Ellipsis:
|
|
if n.Elt != nil {
|
|
r.expr(n.Elt)
|
|
}
|
|
|
|
case *ast.FuncLit:
|
|
r.function(nil, n.Type, n.Body, n)
|
|
|
|
case *ast.CompositeLit:
|
|
if n.Type != nil {
|
|
r.expr(n.Type)
|
|
}
|
|
tv := r.info.Types[n]
|
|
if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
|
|
for _, elt := range n.Elts {
|
|
if kv, ok := elt.(*ast.KeyValueExpr); ok {
|
|
r.expr(kv.Value)
|
|
|
|
// Also uses field kv.Key (non-lexical)
|
|
// id := kv.Key.(*ast.Ident)
|
|
// obj := r.info.Uses[id]
|
|
// logf("use %s = %v (field)\n",
|
|
// id.Name, types.ObjectString(obj, r.qualifier))
|
|
// TODO make a fake FieldVal selection?
|
|
} else {
|
|
r.expr(elt)
|
|
}
|
|
}
|
|
} else {
|
|
r.exprList(n.Elts)
|
|
}
|
|
|
|
case *ast.ParenExpr:
|
|
r.expr(n.X)
|
|
|
|
case *ast.SelectorExpr:
|
|
r.expr(n.X)
|
|
|
|
// Non-lexical reference to field/method, or qualified identifier.
|
|
// if sel, ok := r.info.Selections[n]; ok { // selection
|
|
// switch sel.Kind() {
|
|
// case types.FieldVal:
|
|
// logf("use %s = %v (field)\n",
|
|
// n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
|
|
// case types.MethodExpr, types.MethodVal:
|
|
// logf("use %s = %v (method)\n",
|
|
// n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
|
|
// }
|
|
// } else { // qualified identifier
|
|
// obj := r.info.Uses[n.Sel]
|
|
// logf("use %s = %v (qualified)\n", n.Sel.Name, obj)
|
|
// }
|
|
|
|
case *ast.IndexExpr:
|
|
r.expr(n.X)
|
|
r.expr(n.Index)
|
|
|
|
case *ast.SliceExpr:
|
|
r.expr(n.X)
|
|
if n.Low != nil {
|
|
r.expr(n.Low)
|
|
}
|
|
if n.High != nil {
|
|
r.expr(n.High)
|
|
}
|
|
if n.Max != nil {
|
|
r.expr(n.Max)
|
|
}
|
|
|
|
case *ast.TypeAssertExpr:
|
|
r.expr(n.X)
|
|
if n.Type != nil {
|
|
r.expr(n.Type)
|
|
}
|
|
|
|
case *ast.CallExpr:
|
|
r.expr(n.Fun)
|
|
r.exprList(n.Args)
|
|
|
|
case *ast.StarExpr:
|
|
r.expr(n.X)
|
|
|
|
case *ast.UnaryExpr:
|
|
r.expr(n.X)
|
|
|
|
case *ast.BinaryExpr:
|
|
r.expr(n.X)
|
|
r.expr(n.Y)
|
|
|
|
case *ast.KeyValueExpr:
|
|
r.expr(n.Key)
|
|
r.expr(n.Value)
|
|
|
|
case *ast.ArrayType:
|
|
if n.Len != nil {
|
|
r.expr(n.Len)
|
|
}
|
|
r.expr(n.Elt)
|
|
|
|
case *ast.StructType:
|
|
// Use all the type names, but don't define any fields.
|
|
r.fieldList(n.Fields, false)
|
|
|
|
case *ast.FuncType:
|
|
// Use all the type names, but don't define any vars.
|
|
r.fieldList(n.Params, false)
|
|
r.fieldList(n.Results, false)
|
|
|
|
case *ast.InterfaceType:
|
|
// Use all the type names, but don't define any methods.
|
|
r.fieldList(n.Methods, false)
|
|
|
|
case *ast.MapType:
|
|
r.expr(n.Key)
|
|
r.expr(n.Value)
|
|
|
|
case *ast.ChanType:
|
|
r.expr(n.Value)
|
|
|
|
default:
|
|
panic(n)
|
|
}
|
|
}
|
|
|
|
func (r *resolver) stmtList(list []ast.Stmt) {
|
|
for _, s := range list {
|
|
r.stmt(s)
|
|
}
|
|
}
|
|
|
|
func (r *resolver) stmt(n ast.Stmt) {
|
|
switch n := n.(type) {
|
|
case *ast.BadStmt:
|
|
case *ast.EmptyStmt:
|
|
// nothing to do
|
|
|
|
case *ast.DeclStmt:
|
|
decl := n.Decl.(*ast.GenDecl)
|
|
for _, spec := range decl.Specs {
|
|
switch spec := spec.(type) {
|
|
case *ast.ValueSpec: // const or var
|
|
if spec.Type != nil {
|
|
r.expr(spec.Type)
|
|
}
|
|
r.exprList(spec.Values)
|
|
for _, name := range spec.Names {
|
|
r.define(r.block, name)
|
|
}
|
|
|
|
case *ast.TypeSpec:
|
|
r.define(r.block, spec.Name)
|
|
r.expr(spec.Type)
|
|
}
|
|
}
|
|
|
|
case *ast.LabeledStmt:
|
|
// Also defines label n.Label (non-lexical)
|
|
r.stmt(n.Stmt)
|
|
|
|
case *ast.ExprStmt:
|
|
r.expr(n.X)
|
|
|
|
case *ast.SendStmt:
|
|
r.expr(n.Chan)
|
|
r.expr(n.Value)
|
|
|
|
case *ast.IncDecStmt:
|
|
r.expr(n.X)
|
|
|
|
case *ast.AssignStmt:
|
|
if n.Tok == token.DEFINE {
|
|
r.exprList(n.Rhs)
|
|
for _, lhs := range n.Lhs {
|
|
id := lhs.(*ast.Ident)
|
|
if _, ok := r.info.Defs[id]; ok {
|
|
r.define(r.block, id)
|
|
} else {
|
|
r.use(id, r.block.env())
|
|
}
|
|
}
|
|
} else { // ASSIGN
|
|
r.exprList(n.Lhs)
|
|
r.exprList(n.Rhs)
|
|
}
|
|
|
|
case *ast.GoStmt:
|
|
r.expr(n.Call)
|
|
|
|
case *ast.DeferStmt:
|
|
r.expr(n.Call)
|
|
|
|
case *ast.ReturnStmt:
|
|
r.exprList(n.Results)
|
|
|
|
case *ast.BranchStmt:
|
|
if n.Label != nil {
|
|
// Also uses label n.Label (non-lexical)
|
|
}
|
|
|
|
case *ast.SelectStmt:
|
|
r.stmtList(n.Body.List)
|
|
|
|
case *ast.BlockStmt: // (explicit blocks only)
|
|
savedBlock := r.block // save
|
|
r.setBlock("block", n)
|
|
r.stmtList(n.List)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.IfStmt:
|
|
savedBlock := r.block // save
|
|
r.setBlock("if", n)
|
|
if n.Init != nil {
|
|
r.stmt(n.Init)
|
|
}
|
|
r.expr(n.Cond)
|
|
r.stmt(n.Body) // new block
|
|
if n.Else != nil {
|
|
r.stmt(n.Else)
|
|
}
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.CaseClause:
|
|
savedBlock := r.block // save
|
|
r.setBlock("case", n)
|
|
if obj, ok := r.info.Implicits[n]; ok {
|
|
// e.g.
|
|
// switch y := x.(type) {
|
|
// case T: // we declare an implicit 'var y T' in this block
|
|
// }
|
|
r.defineImplicit(r.block, n, obj.Name())
|
|
}
|
|
r.exprList(n.List)
|
|
r.stmtList(n.Body)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.SwitchStmt:
|
|
savedBlock := r.block // save
|
|
r.setBlock("switch", n)
|
|
if n.Init != nil {
|
|
r.stmt(n.Init)
|
|
}
|
|
if n.Tag != nil {
|
|
r.expr(n.Tag)
|
|
}
|
|
r.stmtList(n.Body.List)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.TypeSwitchStmt:
|
|
savedBlock := r.block // save
|
|
r.setBlock("typeswitch", n)
|
|
if n.Init != nil {
|
|
r.stmt(n.Init)
|
|
}
|
|
if assign, ok := n.Assign.(*ast.AssignStmt); ok { // y := x.(type)
|
|
r.expr(assign.Rhs[0]) // skip y: not a defining ident
|
|
} else {
|
|
r.stmt(n.Assign)
|
|
}
|
|
r.stmtList(n.Body.List)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.CommClause:
|
|
savedBlock := r.block // save
|
|
r.setBlock("case", n)
|
|
if n.Comm != nil {
|
|
r.stmt(n.Comm)
|
|
}
|
|
r.stmtList(n.Body)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.ForStmt:
|
|
savedBlock := r.block // save
|
|
r.setBlock("for", n)
|
|
if n.Init != nil {
|
|
r.stmt(n.Init)
|
|
}
|
|
if n.Cond != nil {
|
|
r.expr(n.Cond)
|
|
}
|
|
if n.Post != nil {
|
|
r.stmt(n.Post)
|
|
}
|
|
r.stmt(n.Body)
|
|
r.block = savedBlock // restore
|
|
|
|
case *ast.RangeStmt:
|
|
r.expr(n.X)
|
|
savedBlock := r.block // save
|
|
r.setBlock("range", n)
|
|
if n.Tok == token.DEFINE {
|
|
if n.Key != nil {
|
|
r.define(r.block, n.Key.(*ast.Ident))
|
|
}
|
|
if n.Value != nil {
|
|
r.define(r.block, n.Value.(*ast.Ident))
|
|
}
|
|
} else {
|
|
if n.Key != nil {
|
|
r.expr(n.Key)
|
|
}
|
|
if n.Value != nil {
|
|
r.expr(n.Value)
|
|
}
|
|
}
|
|
r.stmt(n.Body)
|
|
r.block = savedBlock // restore
|
|
|
|
default:
|
|
panic(n)
|
|
}
|
|
}
|
|
|
|
func (r *resolver) doImport(s *ast.ImportSpec, fileBlock *Block) {
|
|
path, _ := strconv.Unquote(s.Path.Value)
|
|
pkg := r.imports[path]
|
|
if s.Name == nil { // normal
|
|
r.defineImplicit(fileBlock, s, pkg.Name())
|
|
} else if s.Name.Name == "." { // dot import
|
|
for _, name := range pkg.Scope().Names() {
|
|
if ast.IsExported(name) {
|
|
obj := pkg.Scope().Lookup(name)
|
|
r.defineObject(fileBlock, name, obj)
|
|
}
|
|
}
|
|
} else { // renaming import
|
|
r.define(fileBlock, s.Name)
|
|
}
|
|
}
|
|
|
|
func (r *resolver) doPackage(pkg *types.Package, files []*ast.File) {
|
|
r.block = universe
|
|
r.result.Blocks[nil] = universe
|
|
|
|
r.result.PackageBlock = r.setBlock("package", nil)
|
|
|
|
var fileBlocks []*Block
|
|
|
|
// 1. Insert all package-level objects into file and package blocks.
|
|
// (PkgName objects are only inserted into file blocks.)
|
|
for _, f := range files {
|
|
r.block = r.result.PackageBlock
|
|
fileBlock := r.setBlock("file", f) // package is not yet visible to file
|
|
fileBlocks = append(fileBlocks, fileBlock)
|
|
|
|
for _, d := range f.Decls {
|
|
switch d := d.(type) {
|
|
case *ast.GenDecl:
|
|
for _, s := range d.Specs {
|
|
switch s := s.(type) {
|
|
case *ast.ImportSpec:
|
|
r.doImport(s, fileBlock)
|
|
|
|
case *ast.ValueSpec: // const or var
|
|
for _, name := range s.Names {
|
|
r.define(r.result.PackageBlock, name)
|
|
}
|
|
|
|
case *ast.TypeSpec:
|
|
r.define(r.result.PackageBlock, s.Name)
|
|
}
|
|
}
|
|
|
|
case *ast.FuncDecl:
|
|
if d.Recv == nil { // function
|
|
if d.Name.Name != "init" {
|
|
r.define(r.result.PackageBlock, d.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Now resolve bodies of GenDecls and FuncDecls.
|
|
for i, f := range files {
|
|
fileBlock := fileBlocks[i]
|
|
fileBlock.parent = r.result.PackageBlock.env() // make entire package visible to this file
|
|
|
|
for _, d := range f.Decls {
|
|
r.block = fileBlock
|
|
|
|
switch d := d.(type) {
|
|
case *ast.GenDecl:
|
|
for _, s := range d.Specs {
|
|
switch s := s.(type) {
|
|
case *ast.ValueSpec: // const or var
|
|
if s.Type != nil {
|
|
r.expr(s.Type)
|
|
}
|
|
r.exprList(s.Values)
|
|
|
|
case *ast.TypeSpec:
|
|
r.expr(s.Type)
|
|
}
|
|
}
|
|
|
|
case *ast.FuncDecl:
|
|
r.function(d.Recv, d.Type, d.Body, d)
|
|
}
|
|
}
|
|
}
|
|
|
|
r.block = nil
|
|
}
|
|
|
|
// An Info contains the lexical reference structure of a package.
|
|
type Info struct {
|
|
Defs map[types.Object]*Block // maps each object to its defining lexical block
|
|
Refs map[types.Object][]Reference // maps each object to the set of references to it
|
|
Blocks map[ast.Node]*Block // maps declaring syntax to block; nil => universe
|
|
PackageBlock *Block // the package-level lexical block
|
|
}
|
|
|
|
// Structure computes the structure of the lexical environment of the
|
|
// package specified by (pkg, info, files).
|
|
//
|
|
// The info.{Types,Defs,Uses,Implicits} maps must have been populated
|
|
// by the type-checker
|
|
//
|
|
// fset is used for logging.
|
|
//
|
|
func Structure(fset *token.FileSet, pkg *types.Package, info *types.Info, files []*ast.File) *Info {
|
|
r := resolver{
|
|
fset: fset,
|
|
imports: make(map[string]*types.Package),
|
|
result: &Info{
|
|
Defs: make(map[types.Object]*Block),
|
|
Refs: make(map[types.Object][]Reference),
|
|
Blocks: make(map[ast.Node]*Block),
|
|
},
|
|
pkg: pkg,
|
|
info: info,
|
|
}
|
|
|
|
// Build import map for just this package.
|
|
r.imports["unsafe"] = types.Unsafe
|
|
for _, imp := range pkg.Imports() {
|
|
r.imports[imp.Path()] = imp
|
|
}
|
|
|
|
r.doPackage(pkg, files)
|
|
|
|
return r.result
|
|
}
|
|
|
|
// -- 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
|
|
}
|