golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/callgraph/vta/helpers_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package vta 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/ast" 11 "go/parser" 12 "os" 13 "sort" 14 "strings" 15 "testing" 16 17 "golang.org/x/tools/go/callgraph" 18 "golang.org/x/tools/go/ssa/ssautil" 19 20 "golang.org/x/tools/go/loader" 21 "golang.org/x/tools/go/ssa" 22 ) 23 24 // want extracts the contents of the first comment 25 // section starting with "WANT:\n". The returned 26 // content is split into lines without // prefix. 27 func want(f *ast.File) []string { 28 for _, c := range f.Comments { 29 text := strings.TrimSpace(c.Text()) 30 if t := strings.TrimPrefix(text, "WANT:\n"); t != text { 31 return strings.Split(t, "\n") 32 } 33 } 34 return nil 35 } 36 37 // testProg returns an ssa representation of a program at 38 // `path`, assumed to define package "testdata," and the 39 // test want result as list of strings. 40 func testProg(path string, mode ssa.BuilderMode) (*ssa.Program, []string, error) { 41 content, err := os.ReadFile(path) 42 if err != nil { 43 return nil, nil, err 44 } 45 46 conf := loader.Config{ 47 ParserMode: parser.ParseComments, 48 } 49 50 f, err := conf.ParseFile(path, content) 51 if err != nil { 52 return nil, nil, err 53 } 54 55 conf.CreateFromFiles("testdata", f) 56 iprog, err := conf.Load() 57 if err != nil { 58 return nil, nil, err 59 } 60 61 prog := ssautil.CreateProgram(iprog, mode) 62 // Set debug mode to exercise DebugRef instructions. 63 prog.Package(iprog.Created[0].Pkg).SetDebugMode(true) 64 prog.Build() 65 return prog, want(f), nil 66 } 67 68 func firstRegInstr(f *ssa.Function) ssa.Value { 69 for _, b := range f.Blocks { 70 for _, i := range b.Instrs { 71 if v, ok := i.(ssa.Value); ok { 72 return v 73 } 74 } 75 } 76 return nil 77 } 78 79 // funcName returns a name of the function `f` 80 // prefixed with the name of the receiver type. 81 func funcName(f *ssa.Function) string { 82 recv := f.Signature.Recv() 83 if recv == nil { 84 return f.Name() 85 } 86 tp := recv.Type().String() 87 return tp[strings.LastIndex(tp, ".")+1:] + "." + f.Name() 88 } 89 90 // callGraphStr stringifes `g` into a list of strings where 91 // each entry is of the form 92 // 93 // f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ... 94 // 95 // f is a function, cs1, ..., csw are call sites in f, and 96 // f1, f2, ..., fx, fy, ... are the resolved callees. 97 func callGraphStr(g *callgraph.Graph) []string { 98 var gs []string 99 for f, n := range g.Nodes { 100 c := make(map[string][]string) 101 for _, edge := range n.Out { 102 cs := edge.Site.String() 103 c[cs] = append(c[cs], funcName(edge.Callee.Func)) 104 } 105 106 var cs []string 107 for site, fs := range c { 108 sort.Strings(fs) 109 entry := fmt.Sprintf("%v -> %v", site, strings.Join(fs, ", ")) 110 cs = append(cs, entry) 111 } 112 113 sort.Strings(cs) 114 entry := fmt.Sprintf("%v: %v", funcName(f), strings.Join(cs, "; ")) 115 gs = append(gs, entry) 116 } 117 return gs 118 } 119 120 // Logs the functions of prog to t. 121 func logFns(t testing.TB, prog *ssa.Program) { 122 for fn := range ssautil.AllFunctions(prog) { 123 var buf bytes.Buffer 124 fn.WriteTo(&buf) 125 t.Log(buf.String()) 126 } 127 }