File: helpers_test.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.25.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, sid, trixie
  • size: 22,724 kB
  • sloc: javascript: 2,027; asm: 1,645; sh: 166; yacc: 155; makefile: 49; ansic: 8
file content (127 lines) | stat: -rw-r--r-- 3,051 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2021 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 vta

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"os"
	"sort"
	"strings"
	"testing"

	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/ssa/ssautil"

	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/ssa"
)

// want extracts the contents of the first comment
// section starting with "WANT:\n". The returned
// content is split into lines without // prefix.
func want(f *ast.File) []string {
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
			return strings.Split(t, "\n")
		}
	}
	return nil
}

// testProg returns an ssa representation of a program at
// `path`, assumed to define package "testdata," and the
// test want result as list of strings.
func testProg(path string, mode ssa.BuilderMode) (*ssa.Program, []string, error) {
	content, err := os.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}

	conf := loader.Config{
		ParserMode: parser.ParseComments,
	}

	f, err := conf.ParseFile(path, content)
	if err != nil {
		return nil, nil, err
	}

	conf.CreateFromFiles("testdata", f)
	iprog, err := conf.Load()
	if err != nil {
		return nil, nil, err
	}

	prog := ssautil.CreateProgram(iprog, mode)
	// Set debug mode to exercise DebugRef instructions.
	prog.Package(iprog.Created[0].Pkg).SetDebugMode(true)
	prog.Build()
	return prog, want(f), nil
}

func firstRegInstr(f *ssa.Function) ssa.Value {
	for _, b := range f.Blocks {
		for _, i := range b.Instrs {
			if v, ok := i.(ssa.Value); ok {
				return v
			}
		}
	}
	return nil
}

// funcName returns a name of the function `f`
// prefixed with the name of the receiver type.
func funcName(f *ssa.Function) string {
	recv := f.Signature.Recv()
	if recv == nil {
		return f.Name()
	}
	tp := recv.Type().String()
	return tp[strings.LastIndex(tp, ".")+1:] + "." + f.Name()
}

// callGraphStr stringifes `g` into a list of strings where
// each entry is of the form
//
//	f: cs1 -> f1, f2, ...; ...; csw -> fx, fy, ...
//
// f is a function, cs1, ..., csw are call sites in f, and
// f1, f2, ..., fx, fy, ... are the resolved callees.
func callGraphStr(g *callgraph.Graph) []string {
	var gs []string
	for f, n := range g.Nodes {
		c := make(map[string][]string)
		for _, edge := range n.Out {
			cs := edge.Site.String() // TODO(adonovan): handle Site=nil gracefully
			c[cs] = append(c[cs], funcName(edge.Callee.Func))
		}

		var cs []string
		for site, fs := range c {
			sort.Strings(fs)
			entry := fmt.Sprintf("%v -> %v", site, strings.Join(fs, ", "))
			cs = append(cs, entry)
		}

		sort.Strings(cs)
		entry := fmt.Sprintf("%v: %v", funcName(f), strings.Join(cs, "; "))
		gs = append(gs, entry)
	}
	return gs
}

// Logs the functions of prog to t.
func logFns(t testing.TB, prog *ssa.Program) {
	for fn := range ssautil.AllFunctions(prog) {
		var buf bytes.Buffer
		fn.WriteTo(&buf)
		t.Log(buf.String())
	}
}