github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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  	"fmt"
     9  	"go/ast"
    10  	"go/parser"
    11  	"io/ioutil"
    12  	"sort"
    13  	"strings"
    14  
    15  	"github.com/powerman/golang-tools/go/callgraph"
    16  	"github.com/powerman/golang-tools/go/ssa/ssautil"
    17  
    18  	"github.com/powerman/golang-tools/go/loader"
    19  	"github.com/powerman/golang-tools/go/ssa"
    20  )
    21  
    22  // want extracts the contents of the first comment
    23  // section starting with "WANT:\n". The returned
    24  // content is split into lines without // prefix.
    25  func want(f *ast.File) []string {
    26  	for _, c := range f.Comments {
    27  		text := strings.TrimSpace(c.Text())
    28  		if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
    29  			return strings.Split(t, "\n")
    30  		}
    31  	}
    32  	return nil
    33  }
    34  
    35  // testProg returns an ssa representation of a program at
    36  // `path`, assumed to define package "testdata," and the
    37  // test want result as list of strings.
    38  func testProg(path string) (*ssa.Program, []string, error) {
    39  	content, err := ioutil.ReadFile(path)
    40  	if err != nil {
    41  		return nil, nil, err
    42  	}
    43  
    44  	conf := loader.Config{
    45  		ParserMode: parser.ParseComments,
    46  	}
    47  
    48  	f, err := conf.ParseFile(path, content)
    49  	if err != nil {
    50  		return nil, nil, err
    51  	}
    52  
    53  	conf.CreateFromFiles("testdata", f)
    54  	iprog, err := conf.Load()
    55  	if err != nil {
    56  		return nil, nil, err
    57  	}
    58  
    59  	prog := ssautil.CreateProgram(iprog, 0)
    60  	// Set debug mode to exercise DebugRef instructions.
    61  	prog.Package(iprog.Created[0].Pkg).SetDebugMode(true)
    62  	prog.Build()
    63  	return prog, want(f), nil
    64  }
    65  
    66  func firstRegInstr(f *ssa.Function) ssa.Value {
    67  	for _, b := range f.Blocks {
    68  		for _, i := range b.Instrs {
    69  			if v, ok := i.(ssa.Value); ok {
    70  				return v
    71  			}
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  // funcName returns a name of the function `f`
    78  // prefixed with the name of the receiver type.
    79  func funcName(f *ssa.Function) string {
    80  	recv := f.Signature.Recv()
    81  	if recv == nil {
    82  		return f.Name()
    83  	}
    84  	tp := recv.Type().String()
    85  	return tp[strings.LastIndex(tp, ".")+1:] + "." + f.Name()
    86  }
    87  
    88  // callGraphStr stringifes `g` into a list of strings where
    89  // each entry is of the form
    90  //   f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ...
    91  // f is a function, cs1, ..., csw are call sites in f, and
    92  // f1, f2, ..., fx, fy, ... are the resolved callees.
    93  func callGraphStr(g *callgraph.Graph) []string {
    94  	var gs []string
    95  	for f, n := range g.Nodes {
    96  		c := make(map[string][]string)
    97  		for _, edge := range n.Out {
    98  			cs := edge.Site.String()
    99  			c[cs] = append(c[cs], funcName(edge.Callee.Func))
   100  		}
   101  
   102  		var cs []string
   103  		for site, fs := range c {
   104  			sort.Strings(fs)
   105  			entry := fmt.Sprintf("%v -> %v", site, strings.Join(fs, ", "))
   106  			cs = append(cs, entry)
   107  		}
   108  
   109  		sort.Strings(cs)
   110  		entry := fmt.Sprintf("%v: %v", funcName(f), strings.Join(cs, "; "))
   111  		gs = append(gs, entry)
   112  	}
   113  	return gs
   114  }