From f367f012d549dd6623aa2063f348b2b2af809cbc Mon Sep 17 00:00:00 2001 From: Tim King Date: Fri, 30 Jul 2021 15:39:57 -0700 Subject: [PATCH] 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 Run-TryBot: Zvonimir Pavlinovic gopls-CI: kokoro TryBot-Result: Go Bot Trust: Zvonimir Pavlinovic Trust: Roland Shoemaker --- go/callgraph/vta/graph.go | 7 ++++-- go/callgraph/vta/helpers_test.go | 31 +++++++++++++++++++++++ go/callgraph/vta/testdata/go117.go | 40 ++++++++++++++++++++++++++++++ go/callgraph/vta/vta_go117_test.go | 31 +++++++++++++++++++++++ go/callgraph/vta/vta_test.go | 33 ------------------------ 5 files changed, 107 insertions(+), 35 deletions(-) create mode 100644 go/callgraph/vta/testdata/go117.go create mode 100644 go/callgraph/vta/vta_go117_test.go diff --git a/go/callgraph/vta/graph.go b/go/callgraph/vta/graph.go index 1b7b1052ec..6c9e6a576d 100644 --- a/go/callgraph/vta/graph.go +++ b/go/callgraph/vta/graph.go @@ -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)) diff --git a/go/callgraph/vta/helpers_test.go b/go/callgraph/vta/helpers_test.go index 4451f579f6..0e00aeb28a 100644 --- a/go/callgraph/vta/helpers_test.go +++ b/go/callgraph/vta/helpers_test.go @@ -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 +} diff --git a/go/callgraph/vta/testdata/go117.go b/go/callgraph/vta/testdata/go117.go new file mode 100644 index 0000000000..750152e505 --- /dev/null +++ b/go/callgraph/vta/testdata/go117.go @@ -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) diff --git a/go/callgraph/vta/vta_go117_test.go b/go/callgraph/vta/vta_go117_test.go new file mode 100644 index 0000000000..fae657c407 --- /dev/null +++ b/go/callgraph/vta/vta_go117_test.go @@ -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) + } +} diff --git a/go/callgraph/vta/vta_test.go b/go/callgraph/vta/vta_test.go index 79ab31cd72..87b27cba86 100644 --- a/go/callgraph/vta/vta_test.go +++ b/go/callgraph/vta/vta_test.go @@ -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",