Commit c77809e9 authored by Alan Donovan's avatar Alan Donovan

go/types: go/types: add an API test of the Scope type

Also: make (*Scope).Innermost work for Package scopes.

This change is identical to http://go-review.googlesource.com/#/c/11691/,
except for minor changes required by the use of testImporter.

Change-Id: Id07e66f78987f7242c2e642dfd6ee613676e10e5
Reviewed-on: https://go-review.googlesource.com/11714Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 34846aef
......@@ -12,6 +12,8 @@ import (
"go/parser"
"go/token"
"internal/testenv"
"reflect"
"regexp"
"strings"
"testing"
......@@ -942,3 +944,101 @@ func sameSlice(a, b []int) bool {
}
return true
}
// TestScopeLookupParent ensures that (*Scope).LookupParent returns
// the correct result at various positions with the source.
func TestScopeLookupParent(t *testing.T) {
fset := token.NewFileSet()
imports := make(testImporter)
conf := Config{Importer: imports}
mustParse := func(src string) *ast.File {
f, err := parser.ParseFile(fset, "dummy.go", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
return f
}
var info Info
makePkg := func(path string, files ...*ast.File) {
imports[path], _ = conf.Check(path, fset, files, &info)
}
makePkg("lib", mustParse("package lib; var X int"))
// Each /*name=kind:line*/ comment makes the test look up the
// name at that point and checks that it resolves to a decl of
// the specified kind and line number. "undef" means undefined.
mainSrc := `
package main
import "lib"
var Y = lib.X
func f() {
print(Y) /*Y=var:4*/
z /*z=undef*/ := /*z=undef*/ 1 /*z=var:7*/
print(z)
/*f=func:5*/ /*lib=pkgname:3*/
type /*T=undef*/ T /*T=typename:10*/ *T
}
`
info.Uses = make(map[*ast.Ident]Object)
f := mustParse(mainSrc)
makePkg("main", f)
mainScope := imports["main"].Scope()
rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`)
for _, group := range f.Comments {
for _, comment := range group.List {
// Parse the assertion in the comment.
m := rx.FindStringSubmatch(comment.Text)
if m == nil {
t.Errorf("%s: bad comment: %s",
fset.Position(comment.Pos()), comment.Text)
continue
}
name, want := m[1], m[2]
// Look up the name in the innermost enclosing scope.
inner := mainScope.Innermost(comment.Pos())
if inner == nil {
t.Errorf("%s: at %s: can't find innermost scope",
fset.Position(comment.Pos()), comment.Text)
continue
}
got := "undef"
if _, obj := inner.LookupParent(name, comment.Pos()); obj != nil {
kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
got = fmt.Sprintf("%s:%d", kind, fset.Position(obj.Pos()).Line)
}
if got != want {
t.Errorf("%s: at %s: %s resolved to %s, want %s",
fset.Position(comment.Pos()), comment.Text, name, got, want)
}
}
}
// Check that for each referring identifier,
// a lookup of its name on the innermost
// enclosing scope returns the correct object.
for id, wantObj := range info.Uses {
inner := mainScope.Innermost(id.Pos())
if inner == nil {
t.Errorf("%s: can't find innermost scope enclosing %q",
fset.Position(id.Pos()), id.Name)
continue
}
// Exclude selectors and qualified identifiers---lexical
// refs only. (Ideally, we'd see if the AST parent is a
// SelectorExpr, but that requires PathEnclosingInterval
// from golang.org/x/tools/go/ast/astutil.)
if id.Name == "X" {
continue
}
_, gotObj := inner.LookupParent(id.Name, id.Pos())
if gotObj != wantObj {
t.Errorf("%s: got %v, want %v",
fset.Position(id.Pos()), gotObj, wantObj)
continue
}
}
}
......@@ -126,9 +126,20 @@ func (s *Scope) Contains(pos token.Pos) bool {
// Innermost returns the innermost (child) scope containing
// pos. If pos is not within any scope, the result is nil.
// The result is also nil for the Universe scope.
// The result is guaranteed to be valid only if the type-checked
// AST has complete position information.
func (s *Scope) Innermost(pos token.Pos) *Scope {
// Package scopes do not have extents since they may be
// discontiguous, so iterate over the package's files.
if s.parent == Universe {
for _, s := range s.children {
if inner := s.Innermost(pos); inner != nil {
return inner
}
}
}
if s.Contains(pos) {
for _, s := range s.children {
if s.Contains(pos) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment