go.tools/ssa: move pure-AST functions to importer package.
This slightly simplifies the PathEnclosingInterval spec (and some uncommitted client code of mine). R=gri CC=golang-dev https://golang.org/cl/11305043
This commit is contained in:
parent
a399e26e0e
commit
da3a30b5e1
|
|
@ -1,11 +1,7 @@
|
|||
package ssa
|
||||
package importer
|
||||
|
||||
// This file defines utilities for working with source positions.
|
||||
|
||||
// It has no dependencies on ssa or go/types.
|
||||
// TODO(adonovan): move it somewhere more general,
|
||||
// e.g. go.tools/importer?
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
|
@ -623,3 +619,32 @@ func NodeDescription(n ast.Node) string {
|
|||
}
|
||||
panic(fmt.Sprintf("unexpected node type: %T", n))
|
||||
}
|
||||
|
||||
// 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 PackageInfo and ast.Node that
|
||||
// contain source interval [start, end), and all the node's ancestors
|
||||
// up to the AST root. It searches all ast.Files of all packages in the
|
||||
// Importer imp. exact is defined as for standalone
|
||||
// PathEnclosingInterval.
|
||||
//
|
||||
// The result is (nil, nil, false) if not found.
|
||||
//
|
||||
func (imp *Importer) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
|
||||
for _, info := range imp.Packages {
|
||||
for _, f := range info.Files {
|
||||
if !tokenFileContainsPos(imp.Fset.File(f.Package), start) {
|
||||
continue
|
||||
}
|
||||
if path, exact := PathEnclosingInterval(f, start, end); path != nil {
|
||||
return info, path, exact
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
package importer_test
|
||||
|
||||
// This file defines tests of source utilities.
|
||||
|
||||
// TODO(adonovan): exhaustive tests that run over the whole input
|
||||
// tree, not just handcrafted examples.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// findInterval parses input and returns the [start, end) positions of
|
||||
// the first occurrence of substr in input. f==nil indicates failure;
|
||||
// an error has already been reported in that case.
|
||||
//
|
||||
func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
|
||||
f, err := parser.ParseFile(fset, "<input>", input, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
i := strings.Index(input, substr)
|
||||
if i < 0 {
|
||||
t.Errorf("%q is not a substring of input", substr)
|
||||
f = nil
|
||||
return
|
||||
}
|
||||
|
||||
filePos := fset.File(f.Package)
|
||||
return f, filePos.Pos(i), filePos.Pos(i + len(substr))
|
||||
}
|
||||
|
||||
// Common input for following tests.
|
||||
const input = `
|
||||
// Hello.
|
||||
package main
|
||||
import "fmt"
|
||||
func f() {}
|
||||
func main() {
|
||||
z := (x + y) // add them
|
||||
f() // NB: ExprStmt and its CallExpr have same Pos/End
|
||||
}
|
||||
`
|
||||
|
||||
func TestPathEnclosingInterval_Exact(t *testing.T) {
|
||||
// For the exact tests, we check that a substring is mapped to
|
||||
// the canonical string for the node it denotes.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
node string // complete text of expected containing node
|
||||
}{
|
||||
{"package",
|
||||
input[11 : len(input)-1]},
|
||||
{"\npack",
|
||||
input[11 : len(input)-1]},
|
||||
{"main",
|
||||
"main"},
|
||||
{"import",
|
||||
"import \"fmt\""},
|
||||
{"\"fmt\"",
|
||||
"\"fmt\""},
|
||||
{"\nfunc f() {}\n",
|
||||
"func f() {}"},
|
||||
{"x ",
|
||||
"x"},
|
||||
{" y",
|
||||
"y"},
|
||||
{"z",
|
||||
"z"},
|
||||
{" + ",
|
||||
"x + y"},
|
||||
{" :=",
|
||||
"z := (x + y)"},
|
||||
{"x + y",
|
||||
"x + y"},
|
||||
{"(x + y)",
|
||||
"(x + y)"},
|
||||
{" (x + y) ",
|
||||
"(x + y)"},
|
||||
{" (x + y) // add",
|
||||
"(x + y)"},
|
||||
{"func",
|
||||
"func f() {}"},
|
||||
{"func f() {}",
|
||||
"func f() {}"},
|
||||
{"\nfun",
|
||||
"func f() {}"},
|
||||
{" f",
|
||||
"f"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := importer.PathEnclosingInterval(f, start, end)
|
||||
if !exact {
|
||||
t.Errorf("PathEnclosingInterval(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
if test.node != "" {
|
||||
t.Errorf("PathEnclosingInterval(%q).path: got [], want %q",
|
||||
test.substr, test.node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if got := input[path[0].Pos():path[0].End()]; got != test.node {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)",
|
||||
test.substr, got, test.node, pathToString(path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathEnclosingInterval_Paths(t *testing.T) {
|
||||
// For these tests, we check only the path of the enclosing
|
||||
// node, but not its complete text because it's often quite
|
||||
// large when !exact.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
path string // the pathToString(),exact of the expected path
|
||||
}{
|
||||
{"// add",
|
||||
"[BlockStmt FuncDecl File],false"},
|
||||
{"(x + y",
|
||||
"[ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"x +",
|
||||
"[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"z := (x",
|
||||
"[AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"func f",
|
||||
"[FuncDecl File],false"},
|
||||
{"func f()",
|
||||
"[FuncDecl File],false"},
|
||||
{" f()",
|
||||
"[FuncDecl File],false"},
|
||||
{"() {}",
|
||||
"[FuncDecl File],false"},
|
||||
{"// Hello",
|
||||
"[File],false"},
|
||||
{" f",
|
||||
"[Ident FuncDecl File],true"},
|
||||
{"func ",
|
||||
"[FuncDecl File],true"},
|
||||
{"mai",
|
||||
"[Ident File],true"},
|
||||
{"f() // NB",
|
||||
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := importer.PathEnclosingInterval(f, start, end)
|
||||
if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q",
|
||||
test.substr, got, test.path)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Tests of source.go -----------------------------------------
|
||||
|
||||
func TestEnclosingFunction(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string // the input file
|
||||
substr string // first occurrence of this string denotes interval
|
||||
fn string // name of expected containing function
|
||||
}{
|
||||
// We use distinctive numbers as syntactic landmarks.
|
||||
|
||||
// Ordinary function:
|
||||
{`package main
|
||||
func f() { println(1003) }`,
|
||||
"100", "main.f"},
|
||||
// Methods:
|
||||
{`package main
|
||||
type T int
|
||||
func (t T) f() { println(200) }`,
|
||||
"200", "(main.T).f"},
|
||||
// Function literal:
|
||||
{`package main
|
||||
func f() { println(func() { print(300) }) }`,
|
||||
"300", "func@2.24"},
|
||||
// Doubly nested
|
||||
{`package main
|
||||
func f() { println(func() { print(func() { print(350) })})}`,
|
||||
"350", "func@2.39"},
|
||||
// Implicit init for package-level var initializer.
|
||||
{"package main; var a = 400", "400", "main.init"},
|
||||
// No code for constants:
|
||||
{"package main; const a = 500", "500", "(none)"},
|
||||
// Explicit init()
|
||||
{"package main; func init() { println(600) }", "600", "main.init"},
|
||||
// Multiple explicit init functions:
|
||||
{`package main
|
||||
func init() { println("foo") }
|
||||
func init() { println(800) }`,
|
||||
"800", "main.init"},
|
||||
// init() containing FuncLit.
|
||||
{`package main
|
||||
func init() { println(func(){print(900)}) }`,
|
||||
"900", "func@2.27"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
imp := importer.New(new(importer.Context)) // (NB: no Loader)
|
||||
f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
path, exact := importer.PathEnclosingInterval(f, start, end)
|
||||
if !exact {
|
||||
t.Errorf("EnclosingFunction(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
prog := ssa.NewProgram(imp.Fset, 0)
|
||||
prog.CreatePackages(imp)
|
||||
pkg := prog.Package(info.Pkg)
|
||||
pkg.Build()
|
||||
|
||||
name := "(none)"
|
||||
fn := ssa.EnclosingFunction(pkg, path)
|
||||
if fn != nil {
|
||||
name = fn.String()
|
||||
}
|
||||
|
||||
if name != test.fn {
|
||||
t.Errorf("EnclosingFunction(%q in %q) got %s, want %s",
|
||||
test.substr, test.input, name, test.fn)
|
||||
continue
|
||||
}
|
||||
|
||||
// While we're here: test HasEnclosingFunction.
|
||||
if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) {
|
||||
t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v",
|
||||
test.substr, test.input, has, fn != nil)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ package ssa
|
|||
// - bound method wrappers, for uncalled obj.Method closures.
|
||||
// - indirection wrappers, for calls to T-methods on a *T receiver.
|
||||
|
||||
// TODO(adonovan): rename to methods.go.
|
||||
// TODO(adonovan): rename to wrappers.go when promotion logic has evaporated.
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
|
|
@ -127,6 +127,8 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
|
|||
// buildMethodSet computes the concrete method set for type typ.
|
||||
// It is the implementation of Program.MethodSet.
|
||||
//
|
||||
// TODO(adonovan): use go/types.MethodSet(typ) when it's ready.
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodsMu)
|
||||
//
|
||||
func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||
|
|
|
|||
|
|
@ -7,46 +7,8 @@ import (
|
|||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// imp provides ASTs for the program's packages.
|
||||
//
|
||||
// pkg may be nil if no SSA package has yet been created for the found
|
||||
// package. Call prog.CreatePackages(imp) to avoid this.
|
||||
//
|
||||
// The result is (nil, nil, false) if not found.
|
||||
//
|
||||
func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
|
||||
for importPath, info := range imp.Packages {
|
||||
for _, f := range info.Files {
|
||||
if !tokenFileContainsPos(imp.Fset.File(f.Package), start) {
|
||||
continue
|
||||
}
|
||||
if path, exact := PathEnclosingInterval(f, start, end); path != nil {
|
||||
// TODO(adonovan): return info in lieu
|
||||
// of pkg; remove Prog as a parameter;
|
||||
// move to importer.
|
||||
return prog.PackagesByPath[importPath], path, exact
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// EnclosingFunction returns the function that contains the syntax
|
||||
// node denoted by path.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
package ssa_test
|
||||
|
||||
// This file defines tests of the source and source_ast utilities.
|
||||
|
||||
// TODO(adonovan): exhaustive tests that run over the whole input
|
||||
// tree, not just handcrafted examples.
|
||||
// This file defines tests of source-level debugging utilities.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
|
|
@ -21,267 +16,6 @@ import (
|
|||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
||||
// -------- Tests of source_ast.go -------------------------------------
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// findInterval parses input and returns the [start, end) positions of
|
||||
// the first occurrence of substr in input. f==nil indicates failure;
|
||||
// an error has already been reported in that case.
|
||||
//
|
||||
func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
|
||||
f, err := parser.ParseFile(fset, "<input>", input, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
i := strings.Index(input, substr)
|
||||
if i < 0 {
|
||||
t.Errorf("%q is not a substring of input", substr)
|
||||
f = nil
|
||||
return
|
||||
}
|
||||
|
||||
filePos := fset.File(f.Package)
|
||||
return f, filePos.Pos(i), filePos.Pos(i + len(substr))
|
||||
}
|
||||
|
||||
// Common input for following tests.
|
||||
const input = `
|
||||
// Hello.
|
||||
package main
|
||||
import "fmt"
|
||||
func f() {}
|
||||
func main() {
|
||||
z := (x + y) // add them
|
||||
f() // NB: ExprStmt and its CallExpr have same Pos/End
|
||||
}
|
||||
`
|
||||
|
||||
func TestPathEnclosingInterval_Exact(t *testing.T) {
|
||||
// For the exact tests, we check that a substring is mapped to
|
||||
// the canonical string for the node it denotes.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
node string // complete text of expected containing node
|
||||
}{
|
||||
{"package",
|
||||
input[11 : len(input)-1]},
|
||||
{"\npack",
|
||||
input[11 : len(input)-1]},
|
||||
{"main",
|
||||
"main"},
|
||||
{"import",
|
||||
"import \"fmt\""},
|
||||
{"\"fmt\"",
|
||||
"\"fmt\""},
|
||||
{"\nfunc f() {}\n",
|
||||
"func f() {}"},
|
||||
{"x ",
|
||||
"x"},
|
||||
{" y",
|
||||
"y"},
|
||||
{"z",
|
||||
"z"},
|
||||
{" + ",
|
||||
"x + y"},
|
||||
{" :=",
|
||||
"z := (x + y)"},
|
||||
{"x + y",
|
||||
"x + y"},
|
||||
{"(x + y)",
|
||||
"(x + y)"},
|
||||
{" (x + y) ",
|
||||
"(x + y)"},
|
||||
{" (x + y) // add",
|
||||
"(x + y)"},
|
||||
{"func",
|
||||
"func f() {}"},
|
||||
{"func f() {}",
|
||||
"func f() {}"},
|
||||
{"\nfun",
|
||||
"func f() {}"},
|
||||
{" f",
|
||||
"f"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := ssa.PathEnclosingInterval(f, start, end)
|
||||
if !exact {
|
||||
t.Errorf("PathEnclosingInterval(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
if test.node != "" {
|
||||
t.Errorf("PathEnclosingInterval(%q).path: got [], want %q",
|
||||
test.substr, test.node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if got := input[path[0].Pos():path[0].End()]; got != test.node {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)",
|
||||
test.substr, got, test.node, pathToString(path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathEnclosingInterval_Paths(t *testing.T) {
|
||||
// For these tests, we check only the path of the enclosing
|
||||
// node, but not its complete text because it's often quite
|
||||
// large when !exact.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
path string // the pathToString(),exact of the expected path
|
||||
}{
|
||||
{"// add",
|
||||
"[BlockStmt FuncDecl File],false"},
|
||||
{"(x + y",
|
||||
"[ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"x +",
|
||||
"[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"z := (x",
|
||||
"[AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"func f",
|
||||
"[FuncDecl File],false"},
|
||||
{"func f()",
|
||||
"[FuncDecl File],false"},
|
||||
{" f()",
|
||||
"[FuncDecl File],false"},
|
||||
{"() {}",
|
||||
"[FuncDecl File],false"},
|
||||
{"// Hello",
|
||||
"[File],false"},
|
||||
{" f",
|
||||
"[Ident FuncDecl File],true"},
|
||||
{"func ",
|
||||
"[FuncDecl File],true"},
|
||||
{"mai",
|
||||
"[Ident File],true"},
|
||||
{"f() // NB",
|
||||
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := ssa.PathEnclosingInterval(f, start, end)
|
||||
if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q",
|
||||
test.substr, got, test.path)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Tests of source.go -----------------------------------------
|
||||
|
||||
func TestEnclosingFunction(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string // the input file
|
||||
substr string // first occurrence of this string denotes interval
|
||||
fn string // name of expected containing function
|
||||
}{
|
||||
// We use distinctive numbers as syntactic landmarks.
|
||||
|
||||
// Ordinary function:
|
||||
{`package main
|
||||
func f() { println(1003) }`,
|
||||
"100", "main.f"},
|
||||
// Methods:
|
||||
{`package main
|
||||
type T int
|
||||
func (t T) f() { println(200) }`,
|
||||
"200", "(main.T).f"},
|
||||
// Function literal:
|
||||
{`package main
|
||||
func f() { println(func() { print(300) }) }`,
|
||||
"300", "func@2.24"},
|
||||
// Doubly nested
|
||||
{`package main
|
||||
func f() { println(func() { print(func() { print(350) })})}`,
|
||||
"350", "func@2.39"},
|
||||
// Implicit init for package-level var initializer.
|
||||
{"package main; var a = 400", "400", "main.init"},
|
||||
// No code for constants:
|
||||
{"package main; const a = 500", "500", "(none)"},
|
||||
// Explicit init()
|
||||
{"package main; func init() { println(600) }", "600", "main.init"},
|
||||
// Multiple explicit init functions:
|
||||
{`package main
|
||||
func init() { println("foo") }
|
||||
func init() { println(800) }`,
|
||||
"800", "main.init"},
|
||||
// init() containing FuncLit.
|
||||
{`package main
|
||||
func init() { println(func(){print(900)}) }`,
|
||||
"900", "func@2.27"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
imp := importer.New(new(importer.Context)) // (NB: no Loader)
|
||||
f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
path, exact := ssa.PathEnclosingInterval(f, start, end)
|
||||
if !exact {
|
||||
t.Errorf("EnclosingFunction(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
info, err := imp.CreateSourcePackage("main", []*ast.File{f})
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
prog := ssa.NewProgram(imp.Fset, 0)
|
||||
prog.CreatePackages(imp)
|
||||
pkg := prog.Package(info.Pkg)
|
||||
pkg.Build()
|
||||
|
||||
name := "(none)"
|
||||
fn := ssa.EnclosingFunction(pkg, path)
|
||||
if fn != nil {
|
||||
name = fn.String()
|
||||
}
|
||||
|
||||
if name != test.fn {
|
||||
t.Errorf("EnclosingFunction(%q in %q) got %s, want %s",
|
||||
test.substr, test.input, name, test.fn)
|
||||
continue
|
||||
}
|
||||
|
||||
// While we're here: test HasEnclosingFunction.
|
||||
if has := ssa.HasEnclosingFunction(pkg, path); has != (fn != nil) {
|
||||
t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v",
|
||||
test.substr, test.input, has, fn != nil)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjValueLookup(t *testing.T) {
|
||||
imp := importer.New(new(importer.Context)) // (uses GCImporter)
|
||||
f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.DeclarationErrors|parser.ParseComments)
|
||||
|
|
@ -348,7 +82,7 @@ func TestObjValueLookup(t *testing.T) {
|
|||
// The result varies based on the specific Ident.
|
||||
for _, id := range ids {
|
||||
if obj, ok := info.ObjectOf(id).(*types.Var); ok {
|
||||
ref, _ := ssa.PathEnclosingInterval(f, id.Pos(), id.Pos())
|
||||
ref, _ := importer.PathEnclosingInterval(f, id.Pos(), id.Pos())
|
||||
pos := imp.Fset.Position(id.Pos())
|
||||
exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
|
||||
if exp == "" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue