1
0
mirror of https://github.com/golang/go synced 2024-11-19 10:24:43 -07:00
go/ssa/source.go
Alan Donovan 20029fe5f7 go.tools/ssa: utility functions mapping source intervals to ast.Nodes.
PathEnclosingInterval: 	maps a source position to an ast.Node.
EnclosingFunction:   	finds ssa.Function enclosing an ast.Node.
HasEnclosingFunction:   cheaper impl of EnclosingFunction()!=nil
NodeDescription:        user friendly node type descriptions.

+ tests.

Also: make ssa.Package.TypeInfo field a pointer.

R=gri, r
CC=golang-dev
https://golang.org/cl/9639045
2013-05-28 15:28:46 -04:00

142 lines
4.0 KiB
Go

package ssa
// This file defines utilities for working with source positions.
// TODO(adonovan): move this and source_ast.go to a new subpackage
// since neither depends on SSA internals.
import (
"go/ast"
"go/token"
)
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
p := int(pos)
base := f.Base()
return base <= p && p < base+f.Size()
}
// PathEnclosingInterval returns the Package and ast.Node that
// contain source interval [start, end), and all the node's ancestors
// up to the AST root. It searches all files of all packages in the
// program prog. exact is defined as for standalone
// PathEnclosingInterval.
//
// The result is (nil, nil, false) if not found.
//
func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
for _, pkg = range prog.Packages {
for _, f := range pkg.Files {
if !tokenFileContainsPos(prog.Files.File(f.Package), start) {
continue
}
if path, exact = PathEnclosingInterval(f, start, end); path != nil {
return
}
}
}
return nil, nil, false
}
// EnclosingFunction returns the function that contains the syntax
// node denoted by path.
//
// Syntax associated with package-level variable specifications is
// enclosed by the package's init() function.
//
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet (pkg.BuildPackage()
// has not yet been called).
//
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
fn := findEnclosingPackageLevelFunction(pkg, path)
if fn == nil {
return nil // not in any function
}
// ...then walk down the nested anonymous functions.
n := len(path)
outer:
for i := range path {
if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
for _, anon := range fn.AnonFuncs {
if anon.Pos() == lit.Type.Func {
fn = anon
continue outer
}
}
// SSA function not found:
// - package not yet built, or maybe
// - builder skipped FuncLit in dead block
// (in principle; but currently the Builder
// generates even dead FuncLits).
return nil
}
}
return fn
}
// HasEnclosingFunction returns true if the AST node denoted by path
// is contained within the declaration of some function or
// package-level variable.
//
// Unlike EnclosingFunction, the behaviour of this function does not
// depend on whether SSA code for pkg has been built, so it can be
// used to quickly reject check inputs that will cause
// EnclosingFunction to fail, prior to SSA building.
//
func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
return findEnclosingPackageLevelFunction(pkg, path) != nil
}
// findEnclosingPackageLevelFunction returns the *ssa.Function
// corresponding to the package-level function enclosing path.
//
func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
// Package-level 'var' initializer.
return pkg.Init
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.Name == "init" {
// Explicit init() function.
return pkg.Init
}
// Declared function/method.
return findNamedFunc(pkg, decl.Name.NamePos)
}
}
return nil // not in any function
}
// findNamedFunc returns the named function whose FuncDecl.Ident is at
// position pos.
//
func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// Look at all package members and method sets of named types.
// Not very efficient.
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *Function:
if mem.Pos() == pos {
return mem
}
case *Type:
for _, meth := range mem.PtrMethods {
if meth.Pos() == pos {
return meth
}
}
}
}
return nil
}