]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: move go/types-only Scope methods into scopes2.go
authorRobert Griesemer <gri@golang.org>
Fri, 27 Sep 2024 17:54:26 +0000 (10:54 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 27 Sep 2024 19:24:15 +0000 (19:24 +0000)
Remove them them from types2.

Updates #69673.

Change-Id: I7843f6da1edf3a19f85c61706104d173e04088d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/616261
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/scope.go
src/go/types/api_test.go
src/go/types/scope.go
src/go/types/scope2.go [new file with mode: 0644]
src/go/types/scope2_test.go [new file with mode: 0644]

index e742023135d0dda9a2ea48043c12640c138426f6..4024a3f7c812f8a9ed374fa9cf8e399e55b1b6c6 100644 (file)
@@ -10,8 +10,6 @@ import (
        "fmt"
        "internal/goversion"
        "internal/testenv"
-       "reflect"
-       "regexp"
        "sort"
        "strings"
        "sync"
@@ -1993,162 +1991,6 @@ func sameSlice(a, b []int) bool {
        return true
 }
 
-// TestScopeLookupParent ensures that (*Scope).LookupParent returns
-// the correct result at various positions within the source.
-func TestScopeLookupParent(t *testing.T) {
-       imports := make(testImporter)
-       conf := Config{
-               Importer:    imports,
-               EnableAlias: true, // must match default Universe.Lookup behavior
-       }
-       var info Info
-       makePkg := func(path, src string) {
-               var err error
-               imports[path], err = conf.Check(path, []*syntax.File{mustParse(src)}, &info)
-               if err != nil {
-                       t.Fatal(err)
-               }
-       }
-
-       makePkg("lib", "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.
-       // Note that type switch case clauses with an empty body (but for
-       // comments) need the ";" to ensure that the recorded scope extends
-       // past the comments.
-       mainSrc := `
-/*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
-package main
-
-import "lib"
-import . "lib"
-
-const Pi = 3.1415
-type T struct{}
-var Y, _ = lib.X, X
-
-func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
-       const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
-       type /*t=undef*/ t /*t=typename:14*/ *t
-       print(Y) /*Y=var:10*/
-       x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
-       var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
-
-       var a []int
-       for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
-
-       var i interface{}
-       switch y := i.(type) { /*y=undef*/
-       case /*y=undef*/ int /*y=undef*/ : /*y=var:23*/ ;
-       case float32, /*y=undef*/ float64 /*y=undef*/ : /*y=var:23*/ ;
-       default /*y=undef*/ : /*y=var:23*/
-               println(y)
-       }
-       /*y=undef*/
-
-        switch int := i.(type) {
-        case /*int=typename:0*/ int /*int=typename:0*/ : /*int=var:31*/
-               println(int)
-        default /*int=typename:0*/ : /*int=var:31*/ ;
-        }
-
-       _ = param1
-       _ = res1
-       return
-}
-/*main=undef*/
-`
-
-       info.Uses = make(map[*syntax.Name]Object)
-       makePkg("main", mainSrc)
-       mainScope := imports["main"].Scope()
-
-       rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`)
-
-       base := syntax.NewFileBase("main")
-       syntax.CommentsDo(strings.NewReader(mainSrc), func(line, col uint, text string) {
-               pos := syntax.MakePos(base, line, col)
-
-               // Syntax errors are not comments.
-               if text[0] != '/' {
-                       t.Errorf("%s: %s", pos, text)
-                       return
-               }
-
-               // Parse the assertion in the comment.
-               m := rx.FindStringSubmatch(text)
-               if m == nil {
-                       t.Errorf("%s: bad comment: %s", pos, text)
-                       return
-               }
-               name, want := m[1], m[2]
-
-               // Look up the name in the innermost enclosing scope.
-               inner := mainScope.Innermost(pos)
-               if inner == nil {
-                       t.Errorf("%s: at %s: can't find innermost scope", pos, text)
-                       return
-               }
-               got := "undef"
-               if _, obj := inner.LookupParent(name, pos); obj != nil {
-                       kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types2."))
-                       got = fmt.Sprintf("%s:%d", kind, obj.Pos().Line())
-               }
-               if got != want {
-                       t.Errorf("%s: at %s: %s resolved to %s, want %s", pos, 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", id.Pos(), id.Value)
-                       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.Value == "X" {
-                       continue
-               }
-
-               _, gotObj := inner.LookupParent(id.Value, id.Pos())
-               if gotObj != wantObj {
-                       // Print the scope tree of mainScope in case of error.
-                       var printScopeTree func(indent string, s *Scope)
-                       printScopeTree = func(indent string, s *Scope) {
-                               t.Logf("%sscope %s %v-%v = %v",
-                                       indent,
-                                       ScopeComment(s),
-                                       s.Pos(),
-                                       s.End(),
-                                       s.Names())
-                               for i := range s.NumChildren() {
-                                       printScopeTree(indent+"  ", s.Child(i))
-                               }
-                       }
-                       printScopeTree("", mainScope)
-
-                       t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
-                               id.Pos(),
-                               ScopeComment(inner),
-                               id.Value,
-                               id.Pos(),
-                               gotObj,
-                               wantObj,
-                               ObjectScopePos(wantObj))
-                       continue
-               }
-       }
-}
-
 // newDefined creates a new defined type named T with the given underlying type.
 func newDefined(underlying Type) *Named {
        tname := NewTypeName(nopos, nil, "T", nil)
index 216c6f23b994ddedff45aa3c0703041cd94e368f..eefd8fac5b1376483967b06711edee0316d503d2 100644 (file)
@@ -83,25 +83,6 @@ func (s *Scope) Lookup(name string) Object {
        return obj
 }
 
-// LookupParent follows the parent chain of scopes starting with s until
-// it finds a scope where Lookup(name) returns a non-nil object, and then
-// returns that scope and object. If a valid position pos is provided,
-// only objects that were declared at or before pos are considered.
-// If no such scope and object exists, the result is (nil, nil).
-//
-// Note that obj.Parent() may be different from the returned scope if the
-// object was inserted into the scope and already had a parent at that
-// time (see Insert). This can only happen for dot-imported objects
-// whose parent is the scope of the package that exported them.
-func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) {
-       for ; s != nil; s = s.parent {
-               if obj := s.Lookup(name); obj != nil && (!pos.IsKnown() || cmpPos(obj.scopePos(), pos) <= 0) {
-                       return s, obj
-               }
-       }
-       return nil, nil
-}
-
 // Insert attempts to insert an object obj into scope s.
 // If s already contains an alternative object alt with
 // the same name, Insert leaves s unchanged and returns alt.
@@ -146,47 +127,6 @@ func (s *Scope) insert(name string, obj Object) {
        s.elems[name] = obj
 }
 
-// Pos and End describe the scope's source code extent [pos, end).
-// The results are guaranteed to be valid only if the type-checked
-// AST has complete position information. The extent is undefined
-// for Universe and package scopes.
-func (s *Scope) Pos() syntax.Pos { return s.pos }
-func (s *Scope) End() syntax.Pos { return s.end }
-
-// Contains reports whether pos is within the scope's extent.
-// The result is guaranteed to be valid only if the type-checked
-// AST has complete position information.
-func (s *Scope) Contains(pos syntax.Pos) bool {
-       return cmpPos(s.pos, pos) <= 0 && cmpPos(pos, s.end) < 0
-}
-
-// 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 syntax.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) {
-                               return s.Innermost(pos)
-                       }
-               }
-               return s
-       }
-       return nil
-}
-
 // WriteTo writes a string representation of the scope to w,
 // with the scope elements sorted by name.
 // The level of indentation is controlled by n >= 0, with
index f868338f67ec96476d02e6cf98f27c58b8e170d9..ac1fc63072d961b0cc11504d93c97a737d1a9cdf 100644 (file)
@@ -13,8 +13,6 @@ import (
        "go/token"
        "internal/goversion"
        "internal/testenv"
-       "reflect"
-       "regexp"
        "slices"
        "strings"
        "sync"
@@ -1994,156 +1992,6 @@ 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}
-       var info Info
-       makePkg := func(path string, files ...*ast.File) {
-               var err error
-               imports[path], err = conf.Check(path, fset, files, &info)
-               if err != nil {
-                       t.Fatal(err)
-               }
-       }
-
-       makePkg("lib", mustParse(fset, "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.
-       // Note that type switch case clauses with an empty body (but for
-       // comments) need the ";" to ensure that the recorded scope extends
-       // past the comments.
-       mainSrc := `
-/*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
-package main
-
-import "lib"
-import . "lib"
-
-const Pi = 3.1415
-type T struct{}
-var Y, _ = lib.X, X
-
-func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
-       const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
-       type /*t=undef*/ t /*t=typename:14*/ *t
-       print(Y) /*Y=var:10*/
-       x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
-       var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
-
-       var a []int
-       for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
-
-       var i interface{}
-       switch y := i.(type) { /*y=undef*/
-       case /*y=undef*/ int /*y=undef*/ : /*y=var:23*/ ;
-       case float32, /*y=undef*/ float64 /*y=undef*/ : /*y=var:23*/ ;
-       default /*y=undef*/ : /*y=var:23*/
-               println(y)
-       }
-       /*y=undef*/
-
-        switch int := i.(type) {
-        case /*int=typename:0*/ int /*int=typename:0*/ : /*int=var:31*/
-               println(int)
-        default /*int=typename:0*/ : /*int=var:31*/ ;
-        }
-
-       _ = param1
-       _ = res1
-       return
-}
-/*main=undef*/
-`
-
-       info.Uses = make(map[*ast.Ident]Object)
-       f := mustParse(fset, 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 {
-                       // Print the scope tree of mainScope in case of error.
-                       var printScopeTree func(indent string, s *Scope)
-                       printScopeTree = func(indent string, s *Scope) {
-                               t.Logf("%sscope %s %v-%v = %v",
-                                       indent,
-                                       ScopeComment(s),
-                                       s.Pos(),
-                                       s.End(),
-                                       s.Names())
-                               for i := range s.NumChildren() {
-                                       printScopeTree(indent+"  ", s.Child(i))
-                               }
-                       }
-                       printScopeTree("", mainScope)
-
-                       t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
-                               fset.Position(id.Pos()),
-                               ScopeComment(inner),
-                               id.Name,
-                               id.Pos(),
-                               gotObj,
-                               wantObj,
-                               ObjectScopePos(wantObj))
-                       continue
-               }
-       }
-}
-
 // newDefined creates a new defined type named T with the given underlying type.
 // Helper function for use with TestIncompleteInterfaces only.
 func newDefined(underlying Type) *Named {
index fd2dc6f40fa03dd0d1e12f8676c5cc38620b7c23..6d748009f8f431d5a2090e28d1c724262e1b9038 100644 (file)
@@ -86,25 +86,6 @@ func (s *Scope) Lookup(name string) Object {
        return obj
 }
 
-// LookupParent follows the parent chain of scopes starting with s until
-// it finds a scope where Lookup(name) returns a non-nil object, and then
-// returns that scope and object. If a valid position pos is provided,
-// only objects that were declared at or before pos are considered.
-// If no such scope and object exists, the result is (nil, nil).
-//
-// Note that obj.Parent() may be different from the returned scope if the
-// object was inserted into the scope and already had a parent at that
-// time (see Insert). This can only happen for dot-imported objects
-// whose parent is the scope of the package that exported them.
-func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
-       for ; s != nil; s = s.parent {
-               if obj := s.Lookup(name); obj != nil && (!pos.IsValid() || cmpPos(obj.scopePos(), pos) <= 0) {
-                       return s, obj
-               }
-       }
-       return nil, nil
-}
-
 // Insert attempts to insert an object obj into scope s.
 // If s already contains an alternative object alt with
 // the same name, Insert leaves s unchanged and returns alt.
@@ -149,47 +130,6 @@ func (s *Scope) insert(name string, obj Object) {
        s.elems[name] = obj
 }
 
-// Pos and End describe the scope's source code extent [pos, end).
-// The results are guaranteed to be valid only if the type-checked
-// AST has complete position information. The extent is undefined
-// for Universe and package scopes.
-func (s *Scope) Pos() token.Pos { return s.pos }
-func (s *Scope) End() token.Pos { return s.end }
-
-// Contains reports whether pos is within the scope's extent.
-// The result is guaranteed to be valid only if the type-checked
-// AST has complete position information.
-func (s *Scope) Contains(pos token.Pos) bool {
-       return cmpPos(s.pos, pos) <= 0 && cmpPos(pos, s.end) < 0
-}
-
-// 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) {
-                               return s.Innermost(pos)
-                       }
-               }
-               return s
-       }
-       return nil
-}
-
 // WriteTo writes a string representation of the scope to w,
 // with the scope elements sorted by name.
 // The level of indentation is controlled by n >= 0, with
diff --git a/src/go/types/scope2.go b/src/go/types/scope2.go
new file mode 100644 (file)
index 0000000..de7c503
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements go/types-specific scope methods.
+// These methods do not exist in types2.
+
+package types
+
+import "go/token"
+
+// LookupParent follows the parent chain of scopes starting with s until
+// it finds a scope where Lookup(name) returns a non-nil object, and then
+// returns that scope and object. If a valid position pos is provided,
+// only objects that were declared at or before pos are considered.
+// If no such scope and object exists, the result is (nil, nil).
+// The results are guaranteed to be valid only if the type-checked
+// AST has complete position information.
+//
+// Note that obj.Parent() may be different from the returned scope if the
+// object was inserted into the scope and already had a parent at that
+// time (see Insert). This can only happen for dot-imported objects
+// whose parent is the scope of the package that exported them.
+func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
+       for ; s != nil; s = s.parent {
+               if obj := s.Lookup(name); obj != nil && (!pos.IsValid() || cmpPos(obj.scopePos(), pos) <= 0) {
+                       return s, obj
+               }
+       }
+       return nil, nil
+}
+
+// Pos and End describe the scope's source code extent [pos, end).
+// The results are guaranteed to be valid only if the type-checked
+// AST has complete position information. The extent is undefined
+// for Universe and package scopes.
+func (s *Scope) Pos() token.Pos { return s.pos }
+func (s *Scope) End() token.Pos { return s.end }
+
+// Contains reports whether pos is within the scope's extent.
+// The result is guaranteed to be valid only if the type-checked
+// AST has complete position information.
+func (s *Scope) Contains(pos token.Pos) bool {
+       return cmpPos(s.pos, pos) <= 0 && cmpPos(pos, s.end) < 0
+}
+
+// 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) {
+                               return s.Innermost(pos)
+                       }
+               }
+               return s
+       }
+       return nil
+}
diff --git a/src/go/types/scope2_test.go b/src/go/types/scope2_test.go
new file mode 100644 (file)
index 0000000..df01698
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+       "fmt"
+       "go/ast"
+       "go/token"
+       "reflect"
+       "regexp"
+       "strings"
+       "testing"
+
+       . "go/types"
+)
+
+// 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}
+       var info Info
+       makePkg := func(path string, files ...*ast.File) {
+               var err error
+               imports[path], err = conf.Check(path, fset, files, &info)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       makePkg("lib", mustParse(fset, "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.
+       // Note that type switch case clauses with an empty body (but for
+       // comments) need the ";" to ensure that the recorded scope extends
+       // past the comments.
+       mainSrc := `
+/*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
+package main
+
+import "lib"
+import . "lib"
+
+const Pi = 3.1415
+type T struct{}
+var Y, _ = lib.X, X
+
+func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
+       const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
+       type /*t=undef*/ t /*t=typename:14*/ *t
+       print(Y) /*Y=var:10*/
+       x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
+       var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
+
+       var a []int
+       for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
+
+       var i interface{}
+       switch y := i.(type) { /*y=undef*/
+       case /*y=undef*/ int /*y=undef*/ : /*y=var:23*/ ;
+       case float32, /*y=undef*/ float64 /*y=undef*/ : /*y=var:23*/ ;
+       default /*y=undef*/ : /*y=var:23*/
+               println(y)
+       }
+       /*y=undef*/
+
+        switch int := i.(type) {
+        case /*int=typename:0*/ int /*int=typename:0*/ : /*int=var:31*/
+               println(int)
+        default /*int=typename:0*/ : /*int=var:31*/ ;
+        }
+
+       _ = param1
+       _ = res1
+       return
+}
+/*main=undef*/
+`
+
+       info.Uses = make(map[*ast.Ident]Object)
+       f := mustParse(fset, 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 {
+                       // Print the scope tree of mainScope in case of error.
+                       var printScopeTree func(indent string, s *Scope)
+                       printScopeTree = func(indent string, s *Scope) {
+                               t.Logf("%sscope %s %v-%v = %v",
+                                       indent,
+                                       ScopeComment(s),
+                                       s.Pos(),
+                                       s.End(),
+                                       s.Names())
+                               for i := range s.NumChildren() {
+                                       printScopeTree(indent+"  ", s.Child(i))
+                               }
+                       }
+                       printScopeTree("", mainScope)
+
+                       t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
+                               fset.Position(id.Pos()),
+                               ScopeComment(inner),
+                               id.Name,
+                               id.Pos(),
+                               gotObj,
+                               wantObj,
+                               ObjectScopePos(wantObj))
+                       continue
+               }
+       }
+}