X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.1.0%2Fcmd%2Fguru%2Fcallees.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.1.0%2Fcmd%2Fguru%2Fcallees.go;h=597895770ae7f5e494737a0b53d589d35d06c220;hb=3c06164f15bd10aed7d66b6314764a2961a14762;hp=0000000000000000000000000000000000000000;hpb=0e9c3ceb40901f4d44981c1376cb9e23a248e006;p=dotfiles%2F.git diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/cmd/guru/callees.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/cmd/guru/callees.go new file mode 100644 index 00000000..59789577 --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/cmd/guru/callees.go @@ -0,0 +1,257 @@ +// 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 main + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "sort" + + "golang.org/x/tools/cmd/guru/serial" + "golang.org/x/tools/go/loader" + "golang.org/x/tools/go/pointer" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" +) + +// The callees function reports the possible callees of the function call site +// identified by the specified source location. +func callees(q *Query) error { + lconf := loader.Config{Build: q.Build} + + if err := setPTAScope(&lconf, q.Scope); err != nil { + return err + } + + // Load/parse/type-check the program. + lprog, err := loadWithSoftErrors(&lconf) + if err != nil { + return err + } + + qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos + if err != nil { + return err + } + + // Determine the enclosing call for the specified position. + var e *ast.CallExpr + for _, n := range qpos.path { + if e, _ = n.(*ast.CallExpr); e != nil { + break + } + } + if e == nil { + return fmt.Errorf("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 qpos.info.Types[e.Fun].IsType() { + return fmt.Errorf("this is a type conversion, not a function call") + } + + // Deal with obviously static calls before constructing SSA form. + // Some static calls may yet require SSA construction, + // e.g. f := func(){}; f(). + switch funexpr := unparen(e.Fun).(type) { + case *ast.Ident: + switch obj := qpos.info.Uses[funexpr].(type) { + case *types.Builtin: + // Reject calls to built-ins. + return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name()) + case *types.Func: + // This is a static function call + q.Output(lprog.Fset, &calleesTypesResult{ + site: e, + callee: obj, + }) + return nil + } + case *ast.SelectorExpr: + sel := qpos.info.Selections[funexpr] + if sel == nil { + // qualified identifier. + // May refer to top level function variable + // or to top level function. + callee := qpos.info.Uses[funexpr.Sel] + if obj, ok := callee.(*types.Func); ok { + q.Output(lprog.Fset, &calleesTypesResult{ + site: e, + callee: obj, + }) + return nil + } + } else if sel.Kind() == types.MethodVal { + // Inspect the receiver type of the selected method. + // If it is concrete, the call is statically dispatched. + // (Due to implicit field selections, it is not enough to look + // at sel.Recv(), the type of the actual receiver expression.) + method := sel.Obj().(*types.Func) + recvtype := method.Type().(*types.Signature).Recv().Type() + if !types.IsInterface(recvtype) { + // static method call + q.Output(lprog.Fset, &calleesTypesResult{ + site: e, + callee: method, + }) + return nil + } + } + } + + prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) + + ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) + if err != nil { + return err + } + + pkg := prog.Package(qpos.info.Pkg) + if pkg == nil { + return fmt.Errorf("no SSA package") + } + + // Defer SSA construction till after errors are reported. + prog.Build() + + // Ascertain calling function and call site. + callerFn := ssa.EnclosingFunction(pkg, qpos.path) + if callerFn == nil { + return fmt.Errorf("no SSA function built for this location (dead code?)") + } + + // Find the call site. + site, err := findCallSite(callerFn, e) + if err != nil { + return err + } + + funcs, err := findCallees(ptaConfig, site) + if err != nil { + return err + } + + q.Output(lprog.Fset, &calleesSSAResult{ + site: site, + funcs: funcs, + }) + return nil +} + +func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) { + instr, _ := fn.ValueForExpr(call) + callInstr, _ := instr.(ssa.CallInstruction) + if instr == nil { + return nil, fmt.Errorf("this call site is unreachable in this analysis") + } + return callInstr, nil +} + +func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) { + // Avoid running the pointer analysis for static calls. + if callee := site.Common().StaticCallee(); callee != nil { + switch callee.String() { + case "runtime.SetFinalizer", "(reflect.Value).Call": + // The PTA treats calls to these intrinsics as dynamic. + // TODO(adonovan): avoid reliance on PTA internals. + + default: + return []*ssa.Function{callee}, nil // singleton + } + } + + // Dynamic call: use pointer analysis. + conf.BuildCallGraph = true + cg := ptrAnalysis(conf).CallGraph + cg.DeleteSyntheticNodes() + + // Find all call edges from the site. + n := cg.Nodes[site.Parent()] + if n == nil { + return nil, fmt.Errorf("this call site is unreachable in this analysis") + } + calleesMap := make(map[*ssa.Function]bool) + for _, edge := range n.Out { + if edge.Site == site { + calleesMap[edge.Callee.Func] = true + } + } + + // De-duplicate and sort. + funcs := make([]*ssa.Function, 0, len(calleesMap)) + for f := range calleesMap { + funcs = append(funcs, f) + } + sort.Sort(byFuncPos(funcs)) + return funcs, nil +} + +type calleesSSAResult struct { + site ssa.CallInstruction + funcs []*ssa.Function +} + +type calleesTypesResult struct { + site *ast.CallExpr + callee *types.Func +} + +func (r *calleesSSAResult) PrintPlain(printf printfFunc) { + if len(r.funcs) == 0 { + // dynamic call on a provably nil func/interface + printf(r.site, "%s on nil value", r.site.Common().Description()) + } else { + printf(r.site, "this %s dispatches to:", r.site.Common().Description()) + for _, callee := range r.funcs { + printf(callee, "\t%s", callee) + } + } +} + +func (r *calleesSSAResult) JSON(fset *token.FileSet) []byte { + j := &serial.Callees{ + Pos: fset.Position(r.site.Pos()).String(), + Desc: r.site.Common().Description(), + } + for _, callee := range r.funcs { + j.Callees = append(j.Callees, &serial.Callee{ + Name: callee.String(), + Pos: fset.Position(callee.Pos()).String(), + }) + } + return toJSON(j) +} + +func (r *calleesTypesResult) PrintPlain(printf printfFunc) { + printf(r.site, "this static function call dispatches to:") + printf(r.callee, "\t%s", r.callee.FullName()) +} + +func (r *calleesTypesResult) JSON(fset *token.FileSet) []byte { + j := &serial.Callees{ + Pos: fset.Position(r.site.Pos()).String(), + Desc: "static function call", + } + j.Callees = []*serial.Callee{ + { + Name: r.callee.FullName(), + Pos: fset.Position(r.callee.Pos()).String(), + }, + } + return toJSON(j) +} + +// NB: byFuncPos is not deterministic across packages since it depends on load order. +// Use lessPos if the tests need it. +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] }