go/callgraph/vta: not panic on the SliceToArrayPointer instruction

No interesting type flows so the change to vta itself is to not reject the instruction.

Updates golang/go#47326

Change-Id: Ifd11a7ef854afaee3978796f3113ca3254301d19
Reviewed-on: https://go-review.googlesource.com/c/tools/+/338849
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Zvonimir Pavlinovic <zpavlinovic@google.com>
Trust: Roland Shoemaker <roland@golang.org>
This commit is contained in:
Tim King 2021-07-30 15:39:57 -07:00 committed by Roland Shoemaker
parent f68a40bc0d
commit f367f012d5
5 changed files with 107 additions and 35 deletions

View File

@ -72,7 +72,7 @@ func (mv mapValue) String() string {
return fmt.Sprintf("MapValue(%v)", mv.Type())
}
// sliceElem node for VTA, modeling reachable slice element types.
// sliceElem node for VTA, modeling reachable slice and array element types.
type sliceElem struct {
typ types.Type
}
@ -346,8 +346,11 @@ func (b *builder) instr(instr ssa.Instruction) {
b.rtrn(i)
case *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeSlice, *ssa.BinOp,
*ssa.Alloc, *ssa.DebugRef, *ssa.Convert, *ssa.Jump, *ssa.If,
*ssa.Slice, *ssa.Range, *ssa.RunDefers:
*ssa.Slice, *ssa.SliceToArrayPointer, *ssa.Range, *ssa.RunDefers:
// No interesting flow here.
// Notes on individual instructions:
// SliceToArrayPointer: t1 = slice to array pointer *[4]T <- []T (t0)
// No interesting flow as sliceArrayElem(t1) == sliceArrayElem(t0).
return
default:
panic(fmt.Sprintf("unsupported instruction %v\n", instr))

View File

@ -5,11 +5,14 @@
package vta
import (
"fmt"
"go/ast"
"go/parser"
"io/ioutil"
"sort"
"strings"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa/ssautil"
"golang.org/x/tools/go/loader"
@ -81,3 +84,31 @@ func funcName(f *ssa.Function) string {
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()
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
}

40
go/callgraph/vta/testdata/go117.go vendored Normal file
View File

@ -0,0 +1,40 @@
// 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.
// go:build ignore
package testdata
type J interface {
Foo()
Bar()
}
type B struct {
p int
}
func (b B) Foo() {}
func (b B) Bar() {}
func Wobble(b *B, s []J) {
x := (*[3]J)(s)
x[1] = b
a := &s[2]
(*a).Bar()
}
// Relevant SSA:
// func Wobble(b *B, s []J):
// t0 = slice to array pointer *[3]J <- []J (s) *[3]J
// t1 = &t0[1:int] *J
// t2 = make J <- *B (b) J
// *t1 = t2
// t3 = &s[2:int] *J
// ...
// WANT:
// Local(t1) -> Slice([]testdata.J)
// Slice([]testdata.J) -> Local(t1), Local(t3)

View File

@ -0,0 +1,31 @@
// 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.
//go:build go1.17
// +build go1.17
package vta
import (
"testing"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/ssa/ssautil"
)
func TestVTACallGraphGo117(t *testing.T) {
file := "testdata/go117.go"
prog, want, err := testProg(file)
if err != nil {
t.Fatalf("couldn't load test file '%s': %s", file, err)
}
if len(want) == 0 {
t.Fatalf("couldn't find want in `%s`", file)
}
g, _ := typePropGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
if gs := vtaGraphStr(g); !subGraph(want, gs) {
t.Errorf("`%s`: want superset of %v;\n got %v", file, want, gs)
}
}

View File

@ -5,46 +5,13 @@
package vta
import (
"fmt"
"sort"
"strings"
"testing"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// 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()
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
}
func TestVTACallGraph(t *testing.T) {
for _, file := range []string{
"testdata/callgraph_static.go",