Example #1
0
// loadProgram loads the specified set of packages (plus their tests)
// and all their dependencies, from source, through the specified build
// context.  Only packages in pkgs will have their functions bodies typechecked.
func loadProgram(ctxt *build.Context, pkgs map[string]bool) (*loader.Program, error) {
	conf := loader.Config{
		Build:         ctxt,
		SourceImports: true,
		ParserMode:    parser.ParseComments,

		// TODO(adonovan): enable this.  Requires making a lot of code more robust!
		AllowErrors: false,
	}

	// Optimization: don't type-check the bodies of functions in our
	// dependencies, since we only need exported package members.
	conf.TypeCheckFuncBodies = func(p string) bool {
		return pkgs[p] || pkgs[strings.TrimSuffix(p, "_test")]
	}

	if Verbose {
		var list []string
		for pkg := range pkgs {
			list = append(list, pkg)
		}
		sort.Strings(list)
		for _, pkg := range list {
			fmt.Fprintf(os.Stderr, "Loading package: %s\n", pkg)
		}
	}

	for pkg := range pkgs {
		if err := conf.ImportWithTests(pkg); err != nil {
			return nil, err
		}
	}
	return conf.Load()
}
Example #2
0
// Oracle annotates `pkg` using go.tools/oracle interface implements detector.
// It uses `scopes` as analysis scope.
// If `scopes` is none of one of `scopes` is zero string, it uses unit tests as scope.
func Oracle(pkg *ast.Package, scopes ...string) error {
	settings := build.Default
	settings.BuildTags = []string{} // TODO
	conf := loader.Config{Build: &settings, SourceImports: true}

	withTests := false
	if len(scopes) == 0 {
		withTests = true
	}
	for _, scope := range scopes {
		if scope == "" {
			withTests = true
		} else {
			conf.Import(scope)
		}
	}
	if withTests {
		conf.ImportWithTests(pkg.Name)
	} else {
		conf.Import(pkg.Name)
	}

	iprog, err := conf.Load()
	if err != nil {
		return fmt.Errorf("oracle annotator: conf load error: %+v", err)
	}
	o, err := oracle.New(iprog, nil, false)
	if err != nil {
		return fmt.Errorf("oracle annotator: create error: %+v", err)
	}
	for _, class := range pkg.Classes {
		qpos, err := oracle.ParseQueryPos(iprog, string(class.Pos), false)
		if err != nil {
			log.Printf("oracle annotator: parse query pos error: %+v, %+v", err, class.Pos)
			continue
		}

		res, err := o.Query("implements", qpos)
		if err != nil {
			return fmt.Errorf("oracle annotator: query error: %+v, %v", err, class.Pos)
		}
		impls := res.Serial().Implements
		for _, target := range impls.AssignableFromPtr {
			addImplements(class, target)
		}
		for _, target := range impls.AssignableFrom {
			addImplements(class, target)
		}
	}
	return nil
}
Example #3
0
// reduceScope is called for one-shot queries that need only a single
// typed package.  It attempts to guess the query package from pos and
// reduce the analysis scope (set of loaded packages) to just that one
// plus (the exported parts of) its dependencies.  It leaves its
// arguments unchanged on failure.
//
// TODO(adonovan): this is a real mess... but it's fast.
//
func reduceScope(pos string, conf *loader.Config) {
	fqpos, err := fastQueryPos(pos)
	if err != nil {
		return // bad query
	}

	// TODO(adonovan): fix: this gives the wrong results for files
	// in non-importable packages such as tests and ad-hoc packages
	// specified as a list of files (incl. the oracle's tests).
	_, importPath, err := guessImportPath(fqpos.fset.File(fqpos.start).Name(), conf.Build)
	if err != nil {
		return // can't find GOPATH dir
	}
	if importPath == "" {
		return
	}

	// Check that it's possible to load the queried package.
	// (e.g. oracle tests contain different 'package' decls in same dir.)
	// Keep consistent with logic in loader/util.go!
	cfg2 := *conf.Build
	cfg2.CgoEnabled = false
	bp, err := cfg2.Import(importPath, "", 0)
	if err != nil {
		return // no files for package
	}

	// Check that the queried file appears in the package:
	// it might be a '// +build ignore' from an ad-hoc main
	// package, e.g. $GOROOT/src/pkg/net/http/triv.go.
	if !pkgContainsFile(bp, fqpos.fset.File(fqpos.start).Name()) {
		return // not found
	}

	conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }

	// Ignore packages specified on command line.
	conf.CreatePkgs = nil
	conf.ImportPkgs = nil

	// Instead load just the one containing the query position
	// (and possibly its corresponding tests/production code).
	// TODO(adonovan): set 'augment' based on which file list
	// contains
	_ = conf.ImportWithTests(importPath) // ignore error
}
Example #4
0
func TestStdlib(t *testing.T) {
	defer func(saved func(format string, args ...interface{})) {
		logf = saved
	}(logf)
	logf = t.Errorf

	ctxt := build.Default // copy

	// Enumerate $GOROOT packages.
	saved := ctxt.GOPATH
	ctxt.GOPATH = "" // disable GOPATH during AllPackages
	pkgs := buildutil.AllPackages(&ctxt)
	ctxt.GOPATH = saved

	// Throw in a number of go.tools packages too.
	pkgs = append(pkgs,
		"code.google.com/p/go.tools/cmd/godoc",
		"code.google.com/p/go.tools/refactor/lexical")

	// Load, parse and type-check the program.
	conf := loader.Config{
		Build:         &ctxt,
		SourceImports: true,
	}
	for _, path := range pkgs {
		if err := conf.ImportWithTests(path); err != nil {
			t.Error(err)
		}
	}

	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %v", err)
	}

	// This test ensures that Structure doesn't panic and that
	// its internal sanity-checks against go/types don't fail.
	for pkg, info := range iprog.AllPackages {
		_ = Structure(iprog.Fset, pkg, &info.Info, info.Files)
	}
}
Example #5
0
func TestStdlib(t *testing.T) {
	runtime.GC()
	t0 := time.Now()
	var memstats runtime.MemStats
	runtime.ReadMemStats(&memstats)
	alloc := memstats.Alloc

	// Load, parse and type-check the program.
	var conf loader.Config
	for _, path := range allPackages() {
		if err := conf.ImportWithTests(path); err != nil {
			t.Error(err)
		}
	}

	prog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %v", err)
	}

	t1 := time.Now()
	runtime.GC()
	runtime.ReadMemStats(&memstats)

	numPkgs := len(prog.AllPackages)
	if want := 205; numPkgs < want {
		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
	}

	// Dump package members.
	if false {
		for pkg := range prog.AllPackages {
			fmt.Printf("Package %s:\n", pkg.Path())
			scope := pkg.Scope()
			for _, name := range scope.Names() {
				if ast.IsExported(name) {
					fmt.Printf("\t%s\n", types.ObjectString(pkg, scope.Lookup(name)))
				}
			}
			fmt.Println()
		}
	}

	// Check that Test functions for io/ioutil, regexp and
	// compress/bzip2 are all simultaneously present.
	// (The apparent cycle formed when augmenting all three of
	// these packages by their tests was the original motivation
	// for reporting b/7114.)
	//
	// compress/bzip2.TestBitReader in bzip2_test.go    imports io/ioutil
	// io/ioutil.TestTempFile       in tempfile_test.go imports regexp
	// regexp.TestRE2Search         in exec_test.go     imports compress/bzip2
	for _, test := range []struct{ pkg, fn string }{
		{"io/ioutil", "TestTempFile"},
		{"regexp", "TestRE2Search"},
		{"compress/bzip2", "TestBitReader"},
	} {
		info := prog.Imported[test.pkg]
		if info == nil {
			t.Errorf("failed to load package %q", test.pkg)
			continue
		}
		obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func)
		if obj == nil {
			t.Errorf("package %q has no func %q", test.pkg, test.fn)
			continue
		}
	}

	// Dump some statistics.

	// determine line count
	var lineCount int
	prog.Fset.Iterate(func(f *token.File) bool {
		lineCount += f.LineCount()
		return true
	})

	t.Log("GOMAXPROCS:           ", runtime.GOMAXPROCS(0))
	t.Log("#Source lines:        ", lineCount)
	t.Log("Load/parse/typecheck: ", t1.Sub(t0))
	t.Log("#MB:                  ", int64(memstats.Alloc-alloc)/1000000)
}