// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package oracle import ( "go/ast" "go/token" "sort" "code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/oracle/json" "code.google.com/p/go.tools/pointer" "code.google.com/p/go.tools/ssa" ) // Callees reports the possible callees of the function call site // identified by the specified source location. // // TODO(adonovan): if a callee is a wrapper, show the callee's callee. // func callees(o *oracle) (queryResult, error) { // Determine the enclosing call for the specified position. var call *ast.CallExpr for _, n := range o.queryPath { if call, _ = n.(*ast.CallExpr); call != nil { break } } if call == nil { return nil, o.errorf(o.queryPath[0], "there is no function call here") } // TODO(adonovan): issue an error if the call is "too far // away" from the current selection, as this most likely is // not what the user intended. // Reject type conversions. if o.queryPkgInfo.IsType(call.Fun) { return nil, o.errorf(call, "this is a type conversion, not a function call") } // Reject calls to built-ins. if b, ok := o.queryPkgInfo.TypeOf(call.Fun).(*types.Builtin); ok { return nil, o.errorf(call, "this is a call to the built-in '%s' operator", b.Name()) } buildSSA(o) // Compute the subgraph of the callgraph for callsite(s) // arising from 'call'. There may be more than one if its // enclosing function was treated context-sensitively. // (Or zero if it was in dead code.) // // The presence of a key indicates this call site is // interesting even if the value is nil. querySites := make(map[pointer.CallSite][]pointer.CallGraphNode) var arbitrarySite pointer.CallSite o.config.CallSite = func(site pointer.CallSite) { if site.Pos() == call.Lparen { // Not a no-op! Ensures key is // present even if value is nil: querySites[site] = querySites[site] arbitrarySite = site } } o.config.Call = func(site pointer.CallSite, callee pointer.CallGraphNode) { if targets, ok := querySites[site]; ok { querySites[site] = append(targets, callee) } } ptrAnalysis(o) if arbitrarySite == nil { return nil, o.errorf(call.Lparen, "this call site is unreachable in this analysis") } // Compute union of callees across all contexts. funcsMap := make(map[*ssa.Function]bool) for _, callees := range querySites { for _, callee := range callees { funcsMap[callee.Func()] = true } } funcs := make([]*ssa.Function, 0, len(funcsMap)) for f := range funcsMap { funcs = append(funcs, f) } sort.Sort(byFuncPos(funcs)) return &calleesResult{ site: arbitrarySite, funcs: funcs, }, nil } type calleesResult struct { site pointer.CallSite funcs []*ssa.Function } func (r *calleesResult) display(printf printfFunc) { if len(r.funcs) == 0 { // dynamic call on a provably nil func/interface printf(r.site, "%s on nil value", r.site.Description()) } else { printf(r.site, "this %s dispatches to:", r.site.Description()) for _, callee := range r.funcs { printf(callee, "\t%s", callee) } } } func (r *calleesResult) toJSON(res *json.Result, fset *token.FileSet) { j := &json.Callees{ Pos: r.site.Caller().Func().Prog.Fset.Position(r.site.Pos()).String(), Desc: r.site.Description(), } for _, callee := range r.funcs { j.Callees = append(j.Callees, &json.CalleesItem{ Name: callee.String(), Pos: callee.Prog.Fset.Position(callee.Pos()).String(), }) } res.Callees = j } type byFuncPos []*ssa.Function func (a byFuncPos) Len() int { return len(a) } func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }