]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: go/types: add an API test of the Scope type
authorAlan Donovan <adonovan@google.com>
Mon, 29 Jun 2015 20:13:09 +0000 (16:13 -0400)
committerAlan Donovan <adonovan@google.com>
Mon, 29 Jun 2015 20:16:18 +0000 (20:16 +0000)
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/11714
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/api_test.go
src/go/types/scope.go

index bdf47e77f153e9dad1b073ef996187cdaef517d5..eeda0d847c371ea55e83ba74abea936f2bf1a6a4 100644 (file)
@@ -12,6 +12,8 @@ import (
        "go/parser"
        "go/token"
        "internal/testenv"
+       "reflect"
+       "regexp"
        "strings"
        "testing"
 
@@ -852,7 +854,7 @@ func TestIssue8518(t *testing.T) {
        }
 
        const libSrc = `
-package a 
+package a
 import "missing"
 const C1 = foo
 const C2 = missing.C
@@ -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
+               }
+       }
+}
index dae5deff8a4cb13c27fc9983eec548844db53945..3502840225f4a8946dd28229948d1e04e7090e62 100644 (file)
@@ -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) {