go/callgraph/vta: avoid panic on missing function definitions

For golang/go#50670

Change-Id: I86d4aee70584cf255f0972e56726b29555120e6d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/380795
Reviewed-by: Guodong Li <guodongli@google.com>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Tim King <taking@google.com>
This commit is contained in:
Zvonimir Pavlinovic 2022-01-25 10:53:53 -08:00
parent 845bb90a1d
commit 0f0bbfd77b
4 changed files with 71 additions and 0 deletions

View File

@ -573,6 +573,13 @@ func (b *builder) call(c ssa.CallInstruction) {
}
func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) {
// When f has no paremeters (including receiver), there is no type
// flow here. Also, f's body and parameters might be missing, such
// as when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
if len(f.Params) == 0 {
return
}
cc := c.Common()
// When c is an unresolved method call (cc.Method != nil), cc.Value contains
// the receiver object rather than cc.Args[0].
@ -585,6 +592,14 @@ func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) {
offset = 1
}
for i, v := range cc.Args {
// Parameters of f might not be available, as in the case
// when vta is used within the golang.org/x/tools/go/analysis
// framework (see github.com/golang/go/issues/50670).
//
// TODO: investigate other cases of missing body and parameters
if len(f.Params) <= i+offset {
return
}
b.addInFlowAliasEdges(b.nodeFromVal(f.Params[i+offset]), b.nodeFromVal(v))
}
}

13
go/callgraph/vta/testdata/src/d/d.go vendored Normal file
View File

@ -0,0 +1,13 @@
package d
func D(i int) int {
return i + 1
}
type Data struct {
V int
}
func (d Data) Do() int {
return d.V - 1
}

8
go/callgraph/vta/testdata/src/t/t.go vendored Normal file
View File

@ -0,0 +1,8 @@
package t
import "d"
func t(i int) int {
data := d.Data{V: i}
return d.D(i) + data.Do()
}

View File

@ -7,6 +7,9 @@ package vta
import (
"testing"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/buildssa"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
@ -76,3 +79,35 @@ func TestVTAProgVsFuncSet(t *testing.T) {
t.Errorf("pruned callgraph %v should contain %v", got, want)
}
}
// TestVTAPanicMissingDefinitions tests if VTA gracefully handles the case
// where VTA panics when a definition of a function or method is not
// available, which can happen when using analysis package. A successful
// test simply does not panic.
func TestVTAPanicMissingDefinitions(t *testing.T) {
run := func(pass *analysis.Pass) (interface{}, error) {
s := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
CallGraph(ssautil.AllFunctions(s.Pkg.Prog), cha.CallGraph(s.Pkg.Prog))
return nil, nil
}
analyzer := &analysis.Analyzer{
Name: "test",
Doc: "test",
Run: run,
Requires: []*analysis.Analyzer{
buildssa.Analyzer,
},
}
testdata := analysistest.TestData()
res := analysistest.Run(t, testdata, analyzer, "t", "d")
if len(res) != 2 {
t.Errorf("want analysis results for 2 packages; got %v", len(res))
}
for _, r := range res {
if r.Err != nil {
t.Errorf("want no error for package %v; got %v", r.Pass.Pkg.Path(), r.Err)
}
}
}