func doOneInput(input, filename string) bool {
	impctx := &importer.Config{Build: &build.Default}
	imp := importer.New(impctx)

	// Parsing.
	f, err := parser.ParseFile(imp.Fset, filename, input, 0)
	if err != nil {
		// TODO(adonovan): err is a scanner error list;
		// display all errors not just first?
		fmt.Println(err)
		return false
	}

	// Create single-file main package and import its dependencies.
	info := imp.CreatePackage("main", f)

	// SSA creation + building.
	prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
	if err := prog.CreatePackages(imp); err != nil {
		fmt.Println(err)
		return false
	}
	prog.BuildAll()

	mainpkg := prog.Package(info.Pkg)
	ptrmain := mainpkg // main package for the pointer analysis
	if mainpkg.Func("main") == nil {
		// No main function; assume it's a test.
		ptrmain = prog.CreateTestMainPackage(mainpkg)
	}

	ok := true

	lineMapping := make(map[string]string) // maps "file:line" to @line tag

	// Parse expectations in this input.
	var exps []*expectation
	re := regexp.MustCompile("// *@([a-z]*) *(.*)$")
	lines := strings.Split(input, "\n")
	for linenum, line := range lines {
		linenum++ // make it 1-based
		if matches := re.FindAllStringSubmatch(line, -1); matches != nil {
			match := matches[0]
			kind, rest := match[1], match[2]
			e := &expectation{kind: kind, filename: filename, linenum: linenum}

			if kind == "line" {
				if rest == "" {
					ok = false
					e.errorf("@%s expectation requires identifier", kind)
				} else {
					lineMapping[fmt.Sprintf("%s:%d", filename, linenum)] = rest
				}
				continue
			}

			if e.needsProbe() && !strings.Contains(line, "print(") {
				ok = false
				e.errorf("@%s expectation must follow call to print(x)", kind)
				continue
			}

			switch kind {
			case "pointsto":
				e.args = split(rest, "|")

			case "types":
				for _, typstr := range split(rest, "|") {
					var t types.Type = types.Typ[types.Invalid] // means "..."
					if typstr != "..." {
						texpr, err := parser.ParseExpr(typstr)
						if err != nil {
							ok = false
							// Don't print err since its location is bad.
							e.errorf("'%s' is not a valid type", typstr)
							continue
						}
						mainFileScope := mainpkg.Object.Scope().Child(0)
						t, _, err = types.EvalNode(imp.Fset, texpr, mainpkg.Object, mainFileScope)
						if err != nil {
							ok = false
							// Don't print err since its location is bad.
							e.errorf("'%s' is not a valid type: %s", typstr, err)
							continue
						}
					}
					e.types = append(e.types, t)
				}

			case "calls":
				e.args = split(rest, "->")
				// TODO(adonovan): eagerly reject the
				// expectation if fn doesn't denote
				// existing function, rather than fail
				// the expectation after analysis.
				if len(e.args) != 2 {
					ok = false
					e.errorf("@calls expectation wants 'caller -> callee' arguments")
					continue
				}

			case "warning":
				lit, err := strconv.Unquote(strings.TrimSpace(rest))
				if err != nil {
					ok = false
					e.errorf("couldn't parse @warning operand: %s", err.Error())
					continue
				}
				e.args = append(e.args, lit)

			default:
				ok = false
				e.errorf("unknown expectation kind: %s", e)
				continue
			}
			exps = append(exps, e)
		}
	}

	var probes []probe
	var log bytes.Buffer

	// Run the analysis.
	config := &pointer.Config{
		Reflection:     true,
		BuildCallGraph: true,
		Mains:          []*ssa.Package{ptrmain},
		Log:            &log,
		Print: func(site *ssa.CallCommon, p pointer.Pointer) {
			probes = append(probes, probe{site, p})
		},
	}

	// Print the log is there was an error or a panic.
	complete := false
	defer func() {
		if !complete || !ok {
			log.WriteTo(os.Stderr)
		}
	}()

	result := pointer.Analyze(config)

	// Check the expectations.
	for _, e := range exps {
		var pr *probe
		if e.needsProbe() {
			if pr = findProbe(prog, probes, e); pr == nil {
				ok = false
				e.errorf("unreachable print() statement has expectation %s", e)
				continue
			}
			if pr.arg0 == nil {
				ok = false
				e.errorf("expectation on non-pointerlike operand: %s", pr.instr.Args[0].Type())
				continue
			}
		}

		switch e.kind {
		case "pointsto":
			if !checkPointsToExpectation(e, pr, lineMapping, prog) {
				ok = false
			}

		case "types":
			if !checkTypesExpectation(e, pr) {
				ok = false
			}

		case "calls":
			if !checkCallsExpectation(prog, e, result.CallGraph) {
				ok = false
			}

		case "warning":
			if !checkWarningExpectation(prog, e, result.Warnings) {
				ok = false
			}
		}
	}

	complete = true

	// ok = false // debugging: uncomment to always see log

	return ok
}
Esempio n. 2
0
// ptrAnalysis runs the pointer analysis and returns its result.
func ptrAnalysis(o *Oracle) *pointer.Result {
	return pointer.Analyze(&o.ptaConfig)
}
Esempio n. 3
0
// This program demonstrates how to use the pointer analysis to
// obtain a conservative call-graph of a Go program.
//
func Example() {
	const myprog = `
package main

import "fmt"

type I interface {
	f()
}

type C struct{}

func (C) f() {
	fmt.Println("C.f()")
}

func main() {
	var i I = C{}
	i.f() // dynamic method call
}
`
	// Construct an importer.
	// Imports will be loaded as if by 'go build'.
	imp := importer.New(&importer.Config{Build: &build.Default})

	// Parse the input file.
	file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, parser.DeclarationErrors)
	if err != nil {
		fmt.Print(err) // parse error
		return
	}

	// Create a "main" package containing one file.
	mainInfo := imp.LoadMainPackage(file)

	// Create SSA-form program representation.
	var mode ssa.BuilderMode
	prog := ssa.NewProgram(imp.Fset, mode)
	if err := prog.CreatePackages(imp); err != nil {
		fmt.Print(err) // type error in some package
		return
	}
	mainPkg := prog.Package(mainInfo.Pkg)

	// Build SSA code for bodies of all functions in the whole program.
	prog.BuildAll()

	// Run the pointer analysis and build the complete callgraph.
	config := &pointer.Config{
		Mains:          []*ssa.Package{mainPkg},
		BuildCallGraph: true,
	}
	result := pointer.Analyze(config)

	// Find edges originating from the main package.
	// By converting to strings, we de-duplicate nodes
	// representing the same function due to context sensitivity.
	var edges []string
	call.GraphVisitEdges(result.CallGraph, func(edge call.Edge) error {
		caller := edge.Caller.Func()
		if caller.Pkg == mainPkg {
			edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func()))
		}
		return nil
	})

	// Print the edges in sorted order.
	sort.Strings(edges)
	for _, edge := range edges {
		fmt.Println(edge)
	}

	// Output:
	// (main.C).f --> fmt.Println
	// main.init --> fmt.init
	// main.main --> (main.C).f
}
Esempio n. 4
0
// ptrAnalysis runs the pointer analysis and returns its result.
func ptrAnalysis(o *Oracle) *pointer.Result {
	start := time.Now()
	result := pointer.Analyze(&o.config)
	o.timers["pointer analysis"] = time.Since(start)
	return result
}