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>
"fmt"
"internal/goversion"
"internal/testenv"
- "reflect"
- "regexp"
"sort"
"strings"
"sync"
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)
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.
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
"go/token"
"internal/goversion"
"internal/testenv"
- "reflect"
- "regexp"
"slices"
"strings"
"sync"
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 {
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.
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
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
+ }
+ }
+}