golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/callgraph/vta/vta_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 "testing" 9 10 "golang.org/x/tools/go/analysis" 11 "golang.org/x/tools/go/analysis/analysistest" 12 "golang.org/x/tools/go/analysis/passes/buildssa" 13 "golang.org/x/tools/go/callgraph/cha" 14 "golang.org/x/tools/go/ssa" 15 "golang.org/x/tools/go/ssa/ssautil" 16 ) 17 18 func TestVTACallGraph(t *testing.T) { 19 for _, file := range []string{ 20 "testdata/src/callgraph_static.go", 21 "testdata/src/callgraph_ho.go", 22 "testdata/src/callgraph_interfaces.go", 23 "testdata/src/callgraph_pointers.go", 24 "testdata/src/callgraph_collections.go", 25 "testdata/src/callgraph_fields.go", 26 "testdata/src/callgraph_field_funcs.go", 27 "testdata/src/callgraph_recursive_types.go", 28 "testdata/src/callgraph_issue_57756.go", 29 "testdata/src/callgraph_comma_maps.go", 30 "testdata/src/callgraph_type_aliases.go", 31 } { 32 t.Run(file, func(t *testing.T) { 33 prog, want, err := testProg(file, ssa.BuilderMode(0)) 34 if err != nil { 35 t.Fatalf("couldn't load test file '%s': %s", file, err) 36 } 37 if len(want) == 0 { 38 t.Fatalf("couldn't find want in `%s`", file) 39 } 40 41 g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog)) 42 got := callGraphStr(g) 43 if diff := setdiff(want, got); len(diff) > 0 { 44 t.Errorf("computed callgraph %v\nshould contain\n%v\n(diff: %v)", got, want, diff) 45 } 46 }) 47 } 48 } 49 50 // TestVTAProgVsFuncSet exemplifies and tests different possibilities 51 // enabled by having an arbitrary function set as input to CallGraph 52 // instead of the whole program (i.e., ssautil.AllFunctions(prog)). 53 func TestVTAProgVsFuncSet(t *testing.T) { 54 prog, want, err := testProg("testdata/src/callgraph_nested_ptr.go", ssa.BuilderMode(0)) 55 if err != nil { 56 t.Fatalf("couldn't load test `testdata/src/callgraph_nested_ptr.go`: %s", err) 57 } 58 if len(want) == 0 { 59 t.Fatal("couldn't find want in `testdata/src/callgraph_nested_ptr.go`") 60 } 61 62 allFuncs := ssautil.AllFunctions(prog) 63 g := CallGraph(allFuncs, cha.CallGraph(prog)) 64 // VTA over the whole program will produce a call graph that 65 // includes Baz:(**i).Foo -> A.Foo, B.Foo. 66 got := callGraphStr(g) 67 if diff := setdiff(want, got); len(diff) > 0 { 68 t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff) 69 } 70 71 // Prune the set of program functions to exclude Bar(). This should 72 // yield a call graph that includes different set of callees for Baz 73 // Baz:(**i).Foo -> A.Foo 74 // 75 // Note that the exclusion of Bar can happen, for instance, if Baz is 76 // considered an entry point of some data flow analysis and Bar is 77 // provably (e.g., using CHA forward reachability) unreachable from Baz. 78 noBarFuncs := make(map[*ssa.Function]bool) 79 for f, in := range allFuncs { 80 noBarFuncs[f] = in && (funcName(f) != "Bar") 81 } 82 want = []string{"Baz: Do(i) -> Do; invoke t2.Foo() -> A.Foo"} 83 g = CallGraph(noBarFuncs, cha.CallGraph(prog)) 84 got = callGraphStr(g) 85 if diff := setdiff(want, got); len(diff) > 0 { 86 t.Errorf("pruned callgraph %v should contain %v (diff: %v)", got, want, diff) 87 } 88 } 89 90 // TestVTAPanicMissingDefinitions tests if VTA gracefully handles the case 91 // where VTA panics when a definition of a function or method is not 92 // available, which can happen when using analysis package. A successful 93 // test simply does not panic. 94 func TestVTAPanicMissingDefinitions(t *testing.T) { 95 run := func(pass *analysis.Pass) (interface{}, error) { 96 s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 97 CallGraph(ssautil.AllFunctions(s.Pkg.Prog), cha.CallGraph(s.Pkg.Prog)) 98 return nil, nil 99 } 100 101 analyzer := &analysis.Analyzer{ 102 Name: "test", 103 Doc: "test", 104 Run: run, 105 Requires: []*analysis.Analyzer{ 106 buildssa.Analyzer, 107 }, 108 } 109 110 testdata := analysistest.TestData() 111 res := analysistest.Run(t, testdata, analyzer, "t", "d") 112 if len(res) != 2 { 113 t.Errorf("want analysis results for 2 packages; got %v", len(res)) 114 } 115 for _, r := range res { 116 if r.Err != nil { 117 t.Errorf("want no error for package %v; got %v", r.Pass.Pkg.Path(), r.Err) 118 } 119 } 120 } 121 122 func TestVTACallGraphGenerics(t *testing.T) { 123 // TODO(zpavlinovic): add more tests 124 files := []string{ 125 "testdata/src/arrays_generics.go", 126 "testdata/src/callgraph_generics.go", 127 "testdata/src/issue63146.go", 128 } 129 for _, file := range files { 130 t.Run(file, func(t *testing.T) { 131 prog, want, err := testProg(file, ssa.InstantiateGenerics) 132 if err != nil { 133 t.Fatalf("couldn't load test file '%s': %s", file, err) 134 } 135 if len(want) == 0 { 136 t.Fatalf("couldn't find want in `%s`", file) 137 } 138 139 g := CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog)) 140 got := callGraphStr(g) 141 if diff := setdiff(want, got); len(diff) != 0 { 142 t.Errorf("computed callgraph %v should contain %v (diff: %v)", got, want, diff) 143 logFns(t, prog) 144 } 145 }) 146 } 147 } 148 149 func TestVTACallGraphGo117(t *testing.T) { 150 file := "testdata/src/go117.go" 151 prog, want, err := testProg(file, ssa.BuilderMode(0)) 152 if err != nil { 153 t.Fatalf("couldn't load test file '%s': %s", file, err) 154 } 155 if len(want) == 0 { 156 t.Fatalf("couldn't find want in `%s`", file) 157 } 158 159 g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog)) 160 got := vtaGraphStr(g) 161 if diff := setdiff(want, got); len(diff) != 0 { 162 t.Errorf("`%s`: want superset of %v;\n got %v", file, want, got) 163 } 164 }