// 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, impcfg *importer.Config, args *[]string) {
	// TODO(adonovan): make the 'args' argument of
	// (*Importer).LoadInitialPackages part of the
	// importer.Config, and inline LoadInitialPackages into
	// NewImporter.  Then we won't need the 'args' argument.

	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(), impcfg.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 importer/util.go!
	ctxt2 := *impcfg.Build
	ctxt2.CgoEnabled = false
	bp, err := ctxt2.Import(importPath, "", 0)
	if err != nil {
		return // no files for package
	}
	_ = bp

	// TODO(adonovan): fix: also check that the queried file appears in the package.
	//  for _, f := range bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles {
	//  	if sameFile(f, fqpos.filename) { goto found }
	//  }
	//  return // not found
	// found:

	impcfg.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
	*args = []string{importPath}
}
Beispiel #2
0
func main() {
	flag.Parse()
	args := flag.Args()

	impctx := importer.Config{Build: &build.Default}

	var debugMode bool
	var mode ssa.BuilderMode
	for _, c := range *buildFlag {
		switch c {
		case 'D':
			debugMode = true
		case 'P':
			mode |= ssa.LogPackages | ssa.BuildSerially
		case 'F':
			mode |= ssa.LogFunctions | ssa.BuildSerially
		case 'S':
			mode |= ssa.LogSource | ssa.BuildSerially
		case 'C':
			mode |= ssa.SanityCheckFunctions
		case 'N':
			mode |= ssa.NaiveForm
		case 'G':
			impctx.Build = nil
		case 'L':
			mode |= ssa.BuildSerially
		default:
			log.Fatalf("Unknown -build option: '%c'.", c)
		}
	}

	var interpMode interp.Mode
	for _, c := range *interpFlag {
		switch c {
		case 'T':
			interpMode |= interp.EnableTracing
		case 'R':
			interpMode |= interp.DisableRecover
		default:
			log.Fatalf("Unknown -interp option: '%c'.", c)
		}
	}

	if len(args) == 0 {
		fmt.Fprint(os.Stderr, usage)
		os.Exit(1)
	}

	// Profiling support.
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// Load, parse and type-check the program.
	imp := importer.New(&impctx)
	infos, args, err := imp.LoadInitialPackages(args)
	if err != nil {
		log.Fatal(err)
	}

	// Create and build SSA-form program representation.
	prog := ssa.NewProgram(imp.Fset, mode)
	if err := prog.CreatePackages(imp); err != nil {
		log.Fatal(err)
	}
	if debugMode {
		for _, pkg := range prog.AllPackages() {
			pkg.SetDebugMode(true)
		}
	}
	prog.BuildAll()

	// Run the interpreter on the first package with a main function.
	if *runFlag {
		var main *ssa.Package
		for _, info := range infos {
			pkg := prog.Package(info.Pkg)
			if pkg.Func("main") != nil || pkg.CreateTestMainFunction() != nil {
				main = pkg
				break
			}
		}
		if main == nil {
			log.Fatal("No main function")
		}
		interp.Interpret(main, interpMode, main.Object.Path(), args)
	}
}