github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/internal/gcimporter/gcimporter_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file is a copy of $GOROOT/src/go/internal/gcimporter/gcimporter_test.go,
     6  // adjusted to make it build with code from (std lib) internal/testenv copied.
     7  
     8  package gcimporter
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"go/build"
    14  	"go/constant"
    15  	"go/types"
    16  	"io/ioutil"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/powerman/golang-tools/internal/testenv"
    26  )
    27  
    28  func TestMain(m *testing.M) {
    29  	testenv.ExitIfSmallMachine()
    30  	os.Exit(m.Run())
    31  }
    32  
    33  // ----------------------------------------------------------------------------
    34  
    35  func needsCompiler(t *testing.T, compiler string) {
    36  	if runtime.Compiler == compiler {
    37  		return
    38  	}
    39  	switch compiler {
    40  	case "gc":
    41  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    42  	}
    43  }
    44  
    45  // compile runs the compiler on filename, with dirname as the working directory,
    46  // and writes the output file to outdirname.
    47  func compile(t *testing.T, dirname, filename, outdirname string) string {
    48  	testenv.NeedsGoBuild(t)
    49  
    50  	// filename must end with ".go"
    51  	if !strings.HasSuffix(filename, ".go") {
    52  		t.Fatalf("filename doesn't end in .go: %s", filename)
    53  	}
    54  	basename := filepath.Base(filename)
    55  	outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
    56  	cmd := exec.Command("go", "tool", "compile", "-p=p", "-o", outname, filename)
    57  	cmd.Dir = dirname
    58  	out, err := cmd.CombinedOutput()
    59  	if err != nil {
    60  		t.Logf("%s", out)
    61  		t.Fatalf("go tool compile %s failed: %s", filename, err)
    62  	}
    63  	return outname
    64  }
    65  
    66  func testPath(t *testing.T, path, srcDir string) *types.Package {
    67  	t0 := time.Now()
    68  	pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil)
    69  	if err != nil {
    70  		t.Errorf("testPath(%s): %s", path, err)
    71  		return nil
    72  	}
    73  	t.Logf("testPath(%s): %v", path, time.Since(t0))
    74  	return pkg
    75  }
    76  
    77  const maxTime = 30 * time.Second
    78  
    79  func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
    80  	dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
    81  	list, err := ioutil.ReadDir(dirname)
    82  	if err != nil {
    83  		t.Fatalf("testDir(%s): %s", dirname, err)
    84  	}
    85  	for _, f := range list {
    86  		if time.Now().After(endTime) {
    87  			t.Log("testing time used up")
    88  			return
    89  		}
    90  		switch {
    91  		case !f.IsDir():
    92  			// try extensions
    93  			for _, ext := range pkgExts {
    94  				if strings.HasSuffix(f.Name(), ext) {
    95  					name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
    96  					if testPath(t, filepath.Join(dir, name), dir) != nil {
    97  						nimports++
    98  					}
    99  				}
   100  			}
   101  		case f.IsDir():
   102  			nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
   103  		}
   104  	}
   105  	return
   106  }
   107  
   108  func mktmpdir(t *testing.T) string {
   109  	tmpdir, err := ioutil.TempDir("", "gcimporter_test")
   110  	if err != nil {
   111  		t.Fatal("mktmpdir:", err)
   112  	}
   113  	if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
   114  		os.RemoveAll(tmpdir)
   115  		t.Fatal("mktmpdir:", err)
   116  	}
   117  	return tmpdir
   118  }
   119  
   120  const testfile = "exports.go"
   121  
   122  func TestImportTestdata(t *testing.T) {
   123  	needsCompiler(t, "gc")
   124  
   125  	tmpdir := mktmpdir(t)
   126  	defer os.RemoveAll(tmpdir)
   127  
   128  	compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
   129  
   130  	// filename should end with ".go"
   131  	filename := testfile[:len(testfile)-3]
   132  	if pkg := testPath(t, "./testdata/"+filename, tmpdir); pkg != nil {
   133  		// The package's Imports list must include all packages
   134  		// explicitly imported by testfile, plus all packages
   135  		// referenced indirectly via exported objects in testfile.
   136  		// With the textual export format (when run against Go1.6),
   137  		// the list may also include additional packages that are
   138  		// not strictly required for import processing alone (they
   139  		// are exported to err "on the safe side").
   140  		// For now, we just test the presence of a few packages
   141  		// that we know are there for sure.
   142  		got := fmt.Sprint(pkg.Imports())
   143  		for _, want := range []string{"go/ast", "go/token"} {
   144  			if !strings.Contains(got, want) {
   145  				t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  func TestVersionHandling(t *testing.T) {
   152  	if debug {
   153  		t.Skip("TestVersionHandling panics in debug mode")
   154  	}
   155  
   156  	// This package only handles gc export data.
   157  	needsCompiler(t, "gc")
   158  
   159  	const dir = "./testdata/versions"
   160  	list, err := ioutil.ReadDir(dir)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  
   165  	tmpdir := mktmpdir(t)
   166  	defer os.RemoveAll(tmpdir)
   167  	corruptdir := filepath.Join(tmpdir, "testdata", "versions")
   168  	if err := os.Mkdir(corruptdir, 0700); err != nil {
   169  		t.Fatal(err)
   170  	}
   171  
   172  	for _, f := range list {
   173  		name := f.Name()
   174  		if !strings.HasSuffix(name, ".a") {
   175  			continue // not a package file
   176  		}
   177  		if strings.Contains(name, "corrupted") {
   178  			continue // don't process a leftover corrupted file
   179  		}
   180  		pkgpath := "./" + name[:len(name)-2]
   181  
   182  		if testing.Verbose() {
   183  			t.Logf("importing %s", name)
   184  		}
   185  
   186  		// test that export data can be imported
   187  		_, err := Import(make(map[string]*types.Package), pkgpath, dir, nil)
   188  		if err != nil {
   189  			// ok to fail if it fails with a newer version error for select files
   190  			if strings.Contains(err.Error(), "newer version") {
   191  				switch name {
   192  				case "test_go1.11_999b.a", "test_go1.11_999i.a":
   193  					continue
   194  				}
   195  				// fall through
   196  			}
   197  			t.Errorf("import %q failed: %v", pkgpath, err)
   198  			continue
   199  		}
   200  
   201  		// create file with corrupted export data
   202  		// 1) read file
   203  		data, err := ioutil.ReadFile(filepath.Join(dir, name))
   204  		if err != nil {
   205  			t.Fatal(err)
   206  		}
   207  		// 2) find export data
   208  		i := bytes.Index(data, []byte("\n$$B\n")) + 5
   209  		j := bytes.Index(data[i:], []byte("\n$$\n")) + i
   210  		if i < 0 || j < 0 || i > j {
   211  			t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
   212  		}
   213  		// 3) corrupt the data (increment every 7th byte)
   214  		for k := j - 13; k >= i; k -= 7 {
   215  			data[k]++
   216  		}
   217  		// 4) write the file
   218  		pkgpath += "_corrupted"
   219  		filename := filepath.Join(corruptdir, pkgpath) + ".a"
   220  		ioutil.WriteFile(filename, data, 0666)
   221  
   222  		// test that importing the corrupted file results in an error
   223  		_, err = Import(make(map[string]*types.Package), pkgpath, corruptdir, nil)
   224  		if err == nil {
   225  			t.Errorf("import corrupted %q succeeded", pkgpath)
   226  		} else if msg := err.Error(); !strings.Contains(msg, "version skew") {
   227  			t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
   228  		}
   229  	}
   230  }
   231  
   232  func TestImportStdLib(t *testing.T) {
   233  	// This package only handles gc export data.
   234  	needsCompiler(t, "gc")
   235  
   236  	dt := maxTime
   237  	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
   238  		dt = 10 * time.Millisecond
   239  	}
   240  	nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
   241  	t.Logf("tested %d imports", nimports)
   242  }
   243  
   244  var importedObjectTests = []struct {
   245  	name string
   246  	want string
   247  }{
   248  	// non-interfaces
   249  	{"crypto.Hash", "type Hash uint"},
   250  	{"go/ast.ObjKind", "type ObjKind int"},
   251  	{"go/types.Qualifier", "type Qualifier func(*Package) string"},
   252  	{"go/types.Comparable", "func Comparable(T Type) bool"},
   253  	{"math.Pi", "const Pi untyped float"},
   254  	{"math.Sin", "func Sin(x float64) float64"},
   255  	{"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
   256  	{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
   257  
   258  	// interfaces
   259  	{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
   260  	{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
   261  	{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
   262  	{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
   263  	{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
   264  	{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
   265  	{"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
   266  }
   267  
   268  // TODO(rsc): Delete this init func after x/tools no longer needs to test successfully with Go 1.17.
   269  func init() {
   270  	if build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1] <= "go1.17" {
   271  		for i := range importedObjectTests {
   272  			if importedObjectTests[i].name == "context.Context" {
   273  				// Expand any to interface{}.
   274  				importedObjectTests[i].want = "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"
   275  			}
   276  		}
   277  	}
   278  }
   279  
   280  func TestImportedTypes(t *testing.T) {
   281  	testenv.NeedsGo1Point(t, 11)
   282  	// This package only handles gc export data.
   283  	needsCompiler(t, "gc")
   284  
   285  	for _, test := range importedObjectTests {
   286  		obj := importObject(t, test.name)
   287  		if obj == nil {
   288  			continue // error reported elsewhere
   289  		}
   290  		got := types.ObjectString(obj, types.RelativeTo(obj.Pkg()))
   291  
   292  		// TODO(rsc): Delete this block once go.dev/cl/368254 lands.
   293  		if got != test.want && test.want == strings.ReplaceAll(got, "interface{}", "any") {
   294  			got = test.want
   295  		}
   296  
   297  		if got != test.want {
   298  			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
   299  		}
   300  
   301  		if named, _ := obj.Type().(*types.Named); named != nil {
   302  			verifyInterfaceMethodRecvs(t, named, 0)
   303  		}
   304  	}
   305  }
   306  
   307  func TestImportedConsts(t *testing.T) {
   308  	testenv.NeedsGo1Point(t, 11)
   309  	tests := []struct {
   310  		name string
   311  		want constant.Kind
   312  	}{
   313  		{"math.Pi", constant.Float},
   314  		{"math.MaxFloat64", constant.Float},
   315  		{"math.MaxInt64", constant.Int},
   316  	}
   317  
   318  	for _, test := range tests {
   319  		obj := importObject(t, test.name)
   320  		if got := obj.(*types.Const).Val().Kind(); got != test.want {
   321  			t.Errorf("%s: imported as constant.Kind(%v), want constant.Kind(%v)", test.name, got, test.want)
   322  		}
   323  	}
   324  }
   325  
   326  // importObject imports the object specified by a name of the form
   327  // <import path>.<object name>, e.g. go/types.Type.
   328  //
   329  // If any errors occur they are reported via t and the resulting object will
   330  // be nil.
   331  func importObject(t *testing.T, name string) types.Object {
   332  	s := strings.Split(name, ".")
   333  	if len(s) != 2 {
   334  		t.Fatal("inconsistent test data")
   335  	}
   336  	importPath := s[0]
   337  	objName := s[1]
   338  
   339  	pkg, err := Import(make(map[string]*types.Package), importPath, ".", nil)
   340  	if err != nil {
   341  		t.Error(err)
   342  		return nil
   343  	}
   344  
   345  	obj := pkg.Scope().Lookup(objName)
   346  	if obj == nil {
   347  		t.Errorf("%s: object not found", name)
   348  		return nil
   349  	}
   350  	return obj
   351  }
   352  
   353  // verifyInterfaceMethodRecvs verifies that method receiver types
   354  // are named if the methods belong to a named interface type.
   355  func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
   356  	// avoid endless recursion in case of an embedding bug that lead to a cycle
   357  	if level > 10 {
   358  		t.Errorf("%s: embeds itself", named)
   359  		return
   360  	}
   361  
   362  	iface, _ := named.Underlying().(*types.Interface)
   363  	if iface == nil {
   364  		return // not an interface
   365  	}
   366  
   367  	// check explicitly declared methods
   368  	for i := 0; i < iface.NumExplicitMethods(); i++ {
   369  		m := iface.ExplicitMethod(i)
   370  		recv := m.Type().(*types.Signature).Recv()
   371  		if recv == nil {
   372  			t.Errorf("%s: missing receiver type", m)
   373  			continue
   374  		}
   375  		if recv.Type() != named {
   376  			t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
   377  		}
   378  	}
   379  
   380  	// check embedded interfaces (if they are named, too)
   381  	for i := 0; i < iface.NumEmbeddeds(); i++ {
   382  		// embedding of interfaces cannot have cycles; recursion will terminate
   383  		if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
   384  			verifyInterfaceMethodRecvs(t, etype, level+1)
   385  		}
   386  	}
   387  }
   388  
   389  func TestIssue5815(t *testing.T) {
   390  	// This package only handles gc export data.
   391  	needsCompiler(t, "gc")
   392  
   393  	pkg := importPkg(t, "strings", ".")
   394  
   395  	scope := pkg.Scope()
   396  	for _, name := range scope.Names() {
   397  		obj := scope.Lookup(name)
   398  		if obj.Pkg() == nil {
   399  			t.Errorf("no pkg for %s", obj)
   400  		}
   401  		if tname, _ := obj.(*types.TypeName); tname != nil {
   402  			named := tname.Type().(*types.Named)
   403  			for i := 0; i < named.NumMethods(); i++ {
   404  				m := named.Method(i)
   405  				if m.Pkg() == nil {
   406  					t.Errorf("no pkg for %s", m)
   407  				}
   408  			}
   409  		}
   410  	}
   411  }
   412  
   413  // Smoke test to ensure that imported methods get the correct package.
   414  func TestCorrectMethodPackage(t *testing.T) {
   415  	// This package only handles gc export data.
   416  	needsCompiler(t, "gc")
   417  
   418  	imports := make(map[string]*types.Package)
   419  	_, err := Import(imports, "net/http", ".", nil)
   420  	if err != nil {
   421  		t.Fatal(err)
   422  	}
   423  
   424  	mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
   425  	mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
   426  	sel := mset.Lookup(nil, "Lock")
   427  	lock := sel.Obj().(*types.Func)
   428  	if got, want := lock.Pkg().Path(), "sync"; got != want {
   429  		t.Errorf("got package path %q; want %q", got, want)
   430  	}
   431  }
   432  
   433  func TestIssue13566(t *testing.T) {
   434  	// This package only handles gc export data.
   435  	needsCompiler(t, "gc")
   436  
   437  	// On windows, we have to set the -D option for the compiler to avoid having a drive
   438  	// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
   439  	if runtime.GOOS == "windows" {
   440  		t.Skip("avoid dealing with relative paths/drive letters on windows")
   441  	}
   442  
   443  	tmpdir := mktmpdir(t)
   444  	defer os.RemoveAll(tmpdir)
   445  	testoutdir := filepath.Join(tmpdir, "testdata")
   446  
   447  	// b.go needs to be compiled from the output directory so that the compiler can
   448  	// find the compiled package a. We pass the full path to compile() so that we
   449  	// don't have to copy the file to that directory.
   450  	bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
   451  	if err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	compile(t, "testdata", "a.go", testoutdir)
   455  	compile(t, testoutdir, bpath, testoutdir)
   456  
   457  	// import must succeed (test for issue at hand)
   458  	pkg := importPkg(t, "./testdata/b", tmpdir)
   459  
   460  	// make sure all indirectly imported packages have names
   461  	for _, imp := range pkg.Imports() {
   462  		if imp.Name() == "" {
   463  			t.Errorf("no name for %s package", imp.Path())
   464  		}
   465  	}
   466  }
   467  
   468  func TestIssue13898(t *testing.T) {
   469  	// This package only handles gc export data.
   470  	needsCompiler(t, "gc")
   471  
   472  	// import go/internal/gcimporter which imports go/types partially
   473  	imports := make(map[string]*types.Package)
   474  	_, err := Import(imports, "go/internal/gcimporter", ".", nil)
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  
   479  	// look for go/types package
   480  	var goTypesPkg *types.Package
   481  	for path, pkg := range imports {
   482  		if path == "go/types" {
   483  			goTypesPkg = pkg
   484  			break
   485  		}
   486  	}
   487  	if goTypesPkg == nil {
   488  		t.Fatal("go/types not found")
   489  	}
   490  
   491  	// look for go/types.Object type
   492  	obj := lookupObj(t, goTypesPkg.Scope(), "Object")
   493  	typ, ok := obj.Type().(*types.Named)
   494  	if !ok {
   495  		t.Fatalf("go/types.Object type is %v; wanted named type", typ)
   496  	}
   497  
   498  	// lookup go/types.Object.Pkg method
   499  	m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
   500  	if m == nil {
   501  		t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
   502  	}
   503  
   504  	// the method must belong to go/types
   505  	if m.Pkg().Path() != "go/types" {
   506  		t.Fatalf("found %v; want go/types", m.Pkg())
   507  	}
   508  }
   509  
   510  func TestIssue15517(t *testing.T) {
   511  	// This package only handles gc export data.
   512  	needsCompiler(t, "gc")
   513  
   514  	// On windows, we have to set the -D option for the compiler to avoid having a drive
   515  	// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
   516  	if runtime.GOOS == "windows" {
   517  		t.Skip("avoid dealing with relative paths/drive letters on windows")
   518  	}
   519  
   520  	tmpdir := mktmpdir(t)
   521  	defer os.RemoveAll(tmpdir)
   522  
   523  	compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
   524  
   525  	// Multiple imports of p must succeed without redeclaration errors.
   526  	// We use an import path that's not cleaned up so that the eventual
   527  	// file path for the package is different from the package path; this
   528  	// will expose the error if it is present.
   529  	//
   530  	// (Issue: Both the textual and the binary importer used the file path
   531  	// of the package to be imported as key into the shared packages map.
   532  	// However, the binary importer then used the package path to identify
   533  	// the imported package to mark it as complete; effectively marking the
   534  	// wrong package as complete. By using an "unclean" package path, the
   535  	// file and package path are different, exposing the problem if present.
   536  	// The same issue occurs with vendoring.)
   537  	imports := make(map[string]*types.Package)
   538  	for i := 0; i < 3; i++ {
   539  		if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil {
   540  			t.Fatal(err)
   541  		}
   542  	}
   543  }
   544  
   545  func TestIssue15920(t *testing.T) {
   546  	// This package only handles gc export data.
   547  	needsCompiler(t, "gc")
   548  
   549  	// On windows, we have to set the -D option for the compiler to avoid having a drive
   550  	// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
   551  	if runtime.GOOS == "windows" {
   552  		t.Skip("avoid dealing with relative paths/drive letters on windows")
   553  	}
   554  
   555  	compileAndImportPkg(t, "issue15920")
   556  }
   557  
   558  func TestIssue20046(t *testing.T) {
   559  	// This package only handles gc export data.
   560  	needsCompiler(t, "gc")
   561  
   562  	// On windows, we have to set the -D option for the compiler to avoid having a drive
   563  	// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
   564  	if runtime.GOOS == "windows" {
   565  		t.Skip("avoid dealing with relative paths/drive letters on windows")
   566  	}
   567  
   568  	// "./issue20046".V.M must exist
   569  	pkg := compileAndImportPkg(t, "issue20046")
   570  	obj := lookupObj(t, pkg.Scope(), "V")
   571  	if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
   572  		t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
   573  	}
   574  }
   575  
   576  func TestIssue25301(t *testing.T) {
   577  	testenv.NeedsGo1Point(t, 11)
   578  	// This package only handles gc export data.
   579  	needsCompiler(t, "gc")
   580  
   581  	// On windows, we have to set the -D option for the compiler to avoid having a drive
   582  	// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
   583  	if runtime.GOOS == "windows" {
   584  		t.Skip("avoid dealing with relative paths/drive letters on windows")
   585  	}
   586  
   587  	compileAndImportPkg(t, "issue25301")
   588  }
   589  
   590  func importPkg(t *testing.T, path, srcDir string) *types.Package {
   591  	pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil)
   592  	if err != nil {
   593  		t.Fatal(err)
   594  	}
   595  	return pkg
   596  }
   597  
   598  func compileAndImportPkg(t *testing.T, name string) *types.Package {
   599  	tmpdir := mktmpdir(t)
   600  	defer os.RemoveAll(tmpdir)
   601  	compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
   602  	return importPkg(t, "./testdata/"+name, tmpdir)
   603  }
   604  
   605  func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
   606  	if obj := scope.Lookup(name); obj != nil {
   607  		return obj
   608  	}
   609  	t.Fatalf("%s not found", name)
   610  	return nil
   611  }