func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type, infer *TypeInfer) *ssa.Function { if meth != nil { return prog.LookupMethod(typ, meth.Pkg(), meth.Name()) } infer.Logger.Fatal(ErrMethodNotFound) return nil }
// Create a pointer.Config whose scope is the initial packages of lprog // and their dependencies. func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) { // TODO(adonovan): the body of this function is essentially // duplicated in all go/pointer clients. Refactor. // For each initial package (specified on the command line), // if it has a main function, analyze that, // otherwise analyze its tests, if any. var testPkgs, mains []*ssa.Package for _, info := range lprog.InitialPackages() { initialPkg := prog.Package(info.Pkg) // Add package to the pointer analysis scope. if initialPkg.Func("main") != nil { mains = append(mains, initialPkg) } else { testPkgs = append(testPkgs, initialPkg) } } if testPkgs != nil { if p := prog.CreateTestMainPackage(testPkgs...); p != nil { mains = append(mains, p) } } if mains == nil { return nil, fmt.Errorf("analysis scope has no main and no tests") } return &pointer.Config{ Log: ptaLog, Reflection: reflection, Mains: mains, }, nil }
// mainPackage returns the main package to analyze. // The resulting package has a main() function. func mainPackage(prog *ssa.Program, tests bool) (*ssa.Package, error) { pkgs := prog.AllPackages() // TODO(adonovan): allow independent control over tests, mains and libraries. // TODO(adonovan): put this logic in a library; we keep reinventing it. if tests { // If -test, use all packages' tests. if len(pkgs) > 0 { if main := prog.CreateTestMainPackage(pkgs...); main != nil { return main, nil } } return nil, fmt.Errorf("no tests") } // Otherwise, use the first package named main. for _, pkg := range pkgs { if pkg.Object.Name() == "main" { if pkg.Func("main") == nil { return nil, fmt.Errorf("no func main() in main package") } return pkg, nil } } return nil, fmt.Errorf("no main package") }
func getFuncs(prog *ssa.Program, pkg *ssa.Package) []*ssa.Function { funcs := make([]*ssa.Function, 0, len(pkg.Members)) for _, mem := range pkg.Members { fn, ok := mem.(*ssa.Function) if ok { if iu := unicode.IsUpper(rune(fn.Name()[0])); iu || *nonExp { funcs = append(funcs, fn) } } typ, ok := mem.(*ssa.Type) if ok { mset := prog.MethodSets.MethodSet(typ.Type()) for i, n := 0, mset.Len(); i < n; i++ { if mf := prog.MethodValue(mset.At(i)); mf != nil { if mfn := mf.Name(); len(mfn) > 0 { if iu := unicode.IsUpper(rune(mfn[0])); iu || *nonExp { funcs = append(funcs, mf) //fmt.Printf("DEBUG method %v %v\n", mfn, mf) } } } } } } return funcs }
// GetMainPkg returns main package of a command. func MainPkg(prog *ssa.Program) *ssa.Package { pkgs := prog.AllPackages() for _, pkg := range pkgs { if pkg.Pkg.Name() == "main" { return pkg } } return nil // Not found }
// FindMains returns the set of all packages loaded into the given // loader.Program which contain main functions func FindMains(p *loader.Program, s *ssa.Program) []*ssa.Package { ips := p.InitialPackages() mains := make([]*ssa.Package, 0, len(ips)) for _, info := range ips { ssaPkg := s.Package(info.Pkg) if ssaPkg.Func("main") != nil { mains = append(mains, ssaPkg) } } return mains }
// ssaValueForExpr returns the ssa.Value of the non-ast.Ident // expression whose path to the root of the AST is path. // func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) { pkg := prog.Package(qinfo.Pkg) pkg.SetDebugMode(true) pkg.Build() fn := ssa.EnclosingFunction(pkg, path) if fn == nil { return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)") } if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil { return v, addr, nil } return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn) }
// FindQueryMethods locates all methods in the given package (assumed to be // package database/sql) with a string parameter named "query". func FindQueryMethods(sql *types.Package, ssa *ssa.Program) []*QueryMethod { methods := make([]*QueryMethod, 0) scope := sql.Scope() for _, name := range scope.Names() { o := scope.Lookup(name) if !o.Exported() { continue } if _, ok := o.(*types.TypeName); !ok { continue } n := o.Type().(*types.Named) for i := 0; i < n.NumMethods(); i++ { m := n.Method(i) if !m.Exported() { continue } s := m.Type().(*types.Signature) if num, ok := FuncHasQuery(s); ok { methods = append(methods, &QueryMethod{ Func: m, SSA: ssa.FuncValue(m), ArgCount: s.Params().Len(), Param: num, }) } } } return methods }
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { fn := prog.FuncValue(obj) // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging if fn == nil { if obj.Name() != "interfaceMethod" { t.Errorf("FuncValue(%s) == nil", obj) } return } if fnobj := fn.Object(); fnobj != obj { t.Errorf("FuncValue(%s).Object() == %s; value was %s", obj, fnobj, fn.Name()) return } if !types.Identical(fn.Type(), obj.Type()) { t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } }
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { c := prog.ConstValue(obj) // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging if c == nil { t.Errorf("ConstValue(%s) == nil", obj) return } if !types.Identical(c.Type(), obj.Type()) { t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) return } if obj.Name() != "nil" { if !exact.Compare(c.Value, token.EQL, obj.Val()) { t.Errorf("ConstValue(%s).Value (%s) != %s", obj, c.Value, obj.Val()) return } } }
// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil. func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst { constants := make(map[ssa.Const]*ssa.NamedConst) for _, pkg := range prog.AllPackages() { for _, mem := range pkg.Members { obj, ok := mem.(*ssa.NamedConst) if !ok { continue } consttype := obj.Type() if !types.AssignableTo(consttype, builtinErrorType) { continue } if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) { continue } constants[*obj.Value] = obj } } return constants }
// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil. func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value { globals := make(map[*ssa.Global]ssa.Value) for _, pkg := range prog.AllPackages() { for _, mem := range pkg.Members { gbl, ok := mem.(*ssa.Global) if !ok { continue } gbltype := gbl.Type() // globals are always pointers if !types.Identical(deref(gbltype), builtinErrorType) { continue } if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) { continue } globals[gbl] = nil } } return globals }
func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) { // The prefix of all assertions messages. prefix := fmt.Sprintf("VarValue(%s @ L%d)", obj, prog.Fset.Position(ref[0].Pos()).Line) v, gotAddr := prog.VarValue(obj, pkg, ref) // Kind is the concrete type of the ssa Value. gotKind := "nil" if v != nil { gotKind = fmt.Sprintf("%T", v)[len("*ssa."):] } // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging // Check the kinds match. // "nil" indicates expected failure (e.g. optimized away). if expKind != gotKind { t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind) } // Check the types match. // If wantAddr, the expected type is the object's address. if v != nil { expType := obj.Type() if wantAddr { expType = types.NewPointer(expType) if !gotAddr { t.Errorf("%s: got value, want address", prefix) } } else if gotAddr { t.Errorf("%s: got address, want value", prefix) } if !types.Identical(v.Type(), expType) { t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) } } }
// Create a pointer.Config whose scope is the initial packages of lprog // and their dependencies. func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) { // For each initial package (specified on the command line), // if it has a main function, analyze that, // otherwise analyze its tests, if any. var mains []*ssa.Package for _, info := range lprog.InitialPackages() { p := prog.Package(info.Pkg) // Add package to the pointer analysis scope. if p.Pkg.Name() == "main" && p.Func("main") != nil { mains = append(mains, p) } else if main := prog.CreateTestMainPackage(p); main != nil { mains = append(mains, main) } } if mains == nil { return nil, fmt.Errorf("analysis scope has no main and no tests") } return &pointer.Config{ Log: ptaLog, Reflection: reflection, Mains: mains, }, nil }
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path // to the root of the AST is path. isAddr reports whether the // ssa.Value is the address denoted by the ast.Ident, not its value. // func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { switch obj := obj.(type) { case *types.Var: pkg := prog.Package(qinfo.Pkg) pkg.Build() if v, addr := prog.VarValue(obj, pkg, path); v != nil { return v, addr, nil } return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) case *types.Func: fn := prog.FuncValue(obj) if fn == nil { return nil, false, fmt.Errorf("%s is an interface method", obj) } // TODO(adonovan): there's no point running PTA on a *Func ident. // Eliminate this feature. return fn, false, nil } panic(obj) }
// Returns true if unused func checkObj(expr *ast.Ident, object types.Object, prog *loader.Program, ssaprog *ssa.Program, fset *token.FileSet) (unused bool) { if _, ok := object.(*types.Var); !ok { if debug { fmt.Println("Skipping object", object) } return false } pkg, node, _ := prog.PathEnclosingInterval(expr.Pos(), expr.End()) spkg := ssaprog.Package(pkg.Pkg) f := ssa.EnclosingFunction(spkg, node) if f == nil { if debug { fmt.Printf("Unknown function %v %v %v %v\n", fset.Position(expr.Pos()), object, pkg, prog) } return false } value, _ := f.ValueForExpr(expr) // Unwrap unops and grab the value inside if v, ok := value.(*ssa.UnOp); ok { if debug { fmt.Println("Unwrapping unop") } value = v.X } if debug { fmt.Printf("%v %v: %v %#v\n", fset.Position(expr.Pos()), expr, object, value) } if _, ok := value.(*ssa.Global); ok { if debug { fmt.Printf(" is global\n") } return false } if value == nil { if debug { fmt.Println("Value is nil", object) } return false } refs := value.Referrers() if refs == nil { if debug { fmt.Println("Referrers is nil", object) } return false } if debug { fmt.Printf(" (refs) %v\n", refs) } hasRef := false for _, r := range *refs { _, ok := r.(*ssa.DebugRef) hasRef = hasRef || !ok if debug && !ok { fmt.Printf("%v %v: %v %v\n", fset.Position(expr.Pos()), expr, object, r) } } if !hasRef { unused = true } return unused }
func findMethod(prog *ssa.Program, meth *types.Func, typ types.Type) *ssa.Function { if meth != nil { fmt.Fprintf(os.Stderr, " ^ finding method for type: %s pkg: %s name: %s\n", typ.String(), meth.Pkg().Name(), meth.Name()) } return prog.LookupMethod(typ, meth.Pkg(), meth.Name()) }
func toSSA(src io.Reader, file, pkg string) (SSA, error) { var fs []Func var conf loader.Config // Parse the file into a ssa file f, _ := conf.ParseFile(file, src) conf.CreateFromFiles("main.go", f) p, _ := conf.Load() buildsanity := content["ssabuild"] == true var ssap *ssa.Program if buildsanity { ssap = ssautil.CreateProgram(p, ssa.SanityCheckFunctions) } else { ssap = ssautil.CreateProgram(p, ssa.NaiveForm) } // Build ssa prog to retrieve all information and the main pkg ssap.Build() mainpkg := ssap.Package(p.InitialPackages()[0].Pkg) for _, m := range mainpkg.Members { if m.Token() == token.FUNC { f, ok := m.(*ssa.Function) if ok { var params []Value for _, p := range f.Params { v := Value{p.Name(), reflect.TypeOf(p).String()} params = append(params, v) } var freevars []Value for _, fv := range f.FreeVars { v := Value{fv.Name(), reflect.TypeOf(fv).String()} freevars = append(freevars, v) } var locals []Value for _, l := range f.Locals { v := Value{l.Name(), reflect.TypeOf(l).String()} locals = append(locals, v) } var blocks []BB for _, b := range f.Blocks { var instrs []Instr for _, i := range b.Instrs { in := Instr{i.String(), reflect.TypeOf(i).String()} instrs = append(instrs, in) } var preds []int for _, p := range b.Preds { preds = append(preds, p.Index) } var succs []int for _, s := range b.Succs { succs = append(succs, s.Index) } bb := BB{b.Index, instrs, preds, succs} blocks = append(blocks, bb) } fn := Func{f.Name(), params, "par_" + f.Name(), freevars, "freevars_" + f.Name(), locals, "locals_" + f.Name(), blocks, "blocks_" + f.Name()} fs = append(fs, fn) } } } return SSA{fs}, nil }