GoPLS Viewer

Home|gopls/go/callgraph/rta/rta_test.go
1// Copyright 2014 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// No testdata on Android.
6
7//go:build !android
8// +build !android
9
10package rta_test
11
12import (
13    "bytes"
14    "fmt"
15    "go/ast"
16    "go/parser"
17    "go/token"
18    "go/types"
19    "os"
20    "sort"
21    "strings"
22    "testing"
23
24    "golang.org/x/tools/go/callgraph"
25    "golang.org/x/tools/go/callgraph/rta"
26    "golang.org/x/tools/go/loader"
27    "golang.org/x/tools/go/ssa"
28    "golang.org/x/tools/go/ssa/ssautil"
29    "golang.org/x/tools/internal/typeparams"
30)
31
32var inputs = []string{
33    "testdata/func.go",
34    "testdata/rtype.go",
35    "testdata/iface.go",
36}
37
38func expectation(f *ast.File) (stringtoken.Pos) {
39    for _c := range f.Comments {
40        text := strings.TrimSpace(c.Text())
41        if t := strings.TrimPrefix(text"WANT:\n"); t != text {
42            return tc.Pos()
43        }
44    }
45    return ""token.NoPos
46}
47
48// TestRTA runs RTA on each file in inputs, prints the results, and
49// compares it with the golden results embedded in the WANT comment at
50// the end of the file.
51//
52// The results string consists of two parts: the set of dynamic call
53// edges, "f --> g", one per line, and the set of reachable functions,
54// one per line.  Each set is sorted.
55func TestRTA(t *testing.T) {
56    for _filename := range inputs {
57        progfmainPkgerr := loadProgInfo(filenamessa.BuilderMode(0))
58        if err != nil {
59            t.Error(err)
60            continue
61        }
62
63        wantpos := expectation(f)
64        if pos == token.NoPos {
65            t.Errorf("No WANT: comment in %s"filename)
66            continue
67        }
68
69        res := rta.Analyze([]*ssa.Function{
70            mainPkg.Func("main"),
71            mainPkg.Func("init"),
72        }, true)
73
74        if got := printResult(resmainPkg.Pkg"dynamic""Dynamic calls"); got != want {
75            t.Errorf("%s: got:\n%s\nwant:\n%s",
76                prog.Fset.Position(pos), gotwant)
77        }
78    }
79}
80
81// TestRTAGenerics is TestRTA specialized for testing generics.
82func TestRTAGenerics(t *testing.T) {
83    if !typeparams.Enabled {
84        t.Skip("TestRTAGenerics requires type parameters")
85    }
86
87    filename := "testdata/generics.go"
88    progfmainPkgerr := loadProgInfo(filenamessa.InstantiateGenerics)
89    if err != nil {
90        t.Fatal(err)
91    }
92
93    wantpos := expectation(f)
94    if pos == token.NoPos {
95        t.Fatalf("No WANT: comment in %s"filename)
96    }
97
98    res := rta.Analyze([]*ssa.Function{
99        mainPkg.Func("main"),
100        mainPkg.Func("init"),
101    }, true)
102
103    if got := printResult(resmainPkg.Pkg"""All calls"); got != want {
104        t.Errorf("%s: got:\n%s\nwant:\n%s",
105            prog.Fset.Position(pos), gotwant)
106    }
107}
108
109func loadProgInfo(filename stringmode ssa.BuilderMode) (*ssa.Program, *ast.File, *ssa.Packageerror) {
110    contenterr := os.ReadFile(filename)
111    if err != nil {
112        return nilnilnilfmt.Errorf("couldn't read file '%s': %s"filenameerr)
113    }
114
115    conf := loader.Config{
116        ParserModeparser.ParseComments,
117    }
118    ferr := conf.ParseFile(filenamecontent)
119    if err != nil {
120        return nilnilnilerr
121    }
122
123    conf.CreateFromFiles("main"f)
124    iprogerr := conf.Load()
125    if err != nil {
126        return nilnilnilerr
127    }
128
129    prog := ssautil.CreateProgram(iprogmode)
130    prog.Build()
131
132    return progfprog.Package(iprog.Created[0].Pkg), nil
133}
134
135// printResult returns a string representation of res, i.e., call graph,
136// reachable functions, and reflect types. For call graph, only edges
137// whose description contains edgeMatch are returned and their string
138// representation is prefixed with a desc line.
139func printResult(res *rta.Resultfrom *types.PackageedgeMatchdesc stringstring {
140    var buf bytes.Buffer
141
142    writeSorted := func(ss []string) {
143        sort.Strings(ss)
144        for _s := range ss {
145            fmt.Fprintf(&buf"  %s\n"s)
146        }
147    }
148
149    buf.WriteString(desc + "\n")
150    var edges []string
151    callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edgeerror {
152        if strings.Contains(e.Description(), edgeMatch) {
153            edges = append(edgesfmt.Sprintf("%s --> %s",
154                e.Caller.Func.RelString(from),
155                e.Callee.Func.RelString(from)))
156        }
157        return nil
158    })
159    writeSorted(edges)
160
161    buf.WriteString("Reachable functions\n")
162    var reachable []string
163    for f := range res.Reachable {
164        reachable = append(reachablef.RelString(from))
165    }
166    writeSorted(reachable)
167
168    buf.WriteString("Reflect types\n")
169    var rtypes []string
170    res.RuntimeTypes.Iterate(func(key types.Typevalue interface{}) {
171        if value == false { // accessible to reflection
172            rtypes = append(rtypestypes.TypeString(keytypes.RelativeTo(from)))
173        }
174    })
175    writeSorted(rtypes)
176
177    return strings.TrimSpace(buf.String())
178}
179
MembersX
expectation.f
TestRTAGenerics.res
bytes
printResult.RangeStmt_4043.f
TestRTAGenerics.err
TestRTAGenerics.got
loadProgInfo.content
printResult.res
testing
loader
TestRTA.t
loadProgInfo.f
printResult.buf
printResult.BlockStmt.RangeStmt_3572.s
TestRTAGenerics.want
ast
os
TestRTAGenerics.prog
TestRTAGenerics.t
printResult.rtypes
expectation.RangeStmt_717.BlockStmt.t
TestRTA.RangeStmt_1274.filename
TestRTA.RangeStmt_1274.BlockStmt.f
ssautil
TestRTA.RangeStmt_1274.BlockStmt.pos
loadProgInfo.filename
loadProgInfo.conf
expectation
TestRTAGenerics
loadProgInfo.mode
TestRTA.RangeStmt_1274.BlockStmt.prog
strings
typeparams
expectation.RangeStmt_717.c
printResult.from
TestRTA
TestRTAGenerics.mainPkg
loadProgInfo.prog
expectation.RangeStmt_717.BlockStmt.text
TestRTA.RangeStmt_1274.BlockStmt.want
TestRTA.RangeStmt_1274.BlockStmt.got
printResult
printResult.reachable
parser
token
sort
TestRTAGenerics.f
TestRTAGenerics.pos
loadProgInfo.err
printResult.edgeMatch
TestRTA.RangeStmt_1274.BlockStmt.err
loadProgInfo
loadProgInfo.iprog
TestRTAGenerics.filename
printResult.edges
TestRTA.RangeStmt_1274.BlockStmt.mainPkg
TestRTA.RangeStmt_1274.BlockStmt.res
printResult.desc
Members
X