| // Copyright 2014 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. |
| |
| package static_test |
| |
| import ( |
| "fmt" |
| "go/parser" |
| "reflect" |
| "sort" |
| "testing" |
| |
| "golang.org/x/tools/go/callgraph" |
| "golang.org/x/tools/go/callgraph/static" |
| "golang.org/x/tools/go/loader" |
| "golang.org/x/tools/go/ssa" |
| "golang.org/x/tools/go/ssa/ssautil" |
| "golang.org/x/tools/internal/typeparams" |
| ) |
| |
| const input = `package P |
| |
| type C int |
| func (C) f() |
| |
| type I interface{f()} |
| |
| func f() { |
| p := func() {} |
| g() |
| p() // SSA constant propagation => static |
| |
| if unknown { |
| p = h |
| } |
| p() // dynamic |
| |
| C(0).f() |
| } |
| |
| func g() { |
| var i I = C(0) |
| i.f() |
| } |
| |
| func h() |
| |
| var unknown bool |
| ` |
| |
| const genericsInput = `package P |
| |
| type I interface { |
| F() |
| } |
| |
| type A struct{} |
| |
| func (a A) F() {} |
| |
| type B struct{} |
| |
| func (b B) F() {} |
| |
| func instantiated[X I](x X) { |
| x.F() |
| } |
| |
| func Bar() {} |
| |
| func f(h func(), a A, b B) { |
| h() |
| |
| instantiated[A](a) |
| instantiated[B](b) |
| } |
| ` |
| |
| func TestStatic(t *testing.T) { |
| for _, e := range []struct { |
| input string |
| want []string |
| // typeparams must be true if input uses type parameters |
| typeparams bool |
| }{ |
| {input, []string{ |
| "(*C).f -> (C).f", |
| "f -> (C).f", |
| "f -> f$1", |
| "f -> g", |
| }, false}, |
| {genericsInput, []string{ |
| "(*A).F -> (A).F", |
| "(*B).F -> (B).F", |
| "f -> instantiated[P.A]", |
| "f -> instantiated[P.B]", |
| "instantiated[P.A] -> (A).F", |
| "instantiated[P.B] -> (B).F", |
| }, true}, |
| } { |
| if e.typeparams && !typeparams.Enabled { |
| // Skip tests with type parameters when the build |
| // environment is not supporting any. |
| continue |
| } |
| |
| conf := loader.Config{ParserMode: parser.ParseComments} |
| f, err := conf.ParseFile("P.go", e.input) |
| if err != nil { |
| t.Error(err) |
| continue |
| } |
| |
| conf.CreateFromFiles("P", f) |
| iprog, err := conf.Load() |
| if err != nil { |
| t.Error(err) |
| continue |
| } |
| |
| P := iprog.Created[0].Pkg |
| |
| prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics) |
| prog.Build() |
| |
| cg := static.CallGraph(prog) |
| |
| var edges []string |
| callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error { |
| edges = append(edges, fmt.Sprintf("%s -> %s", |
| e.Caller.Func.RelString(P), |
| e.Callee.Func.RelString(P))) |
| return nil |
| }) |
| sort.Strings(edges) |
| |
| if !reflect.DeepEqual(edges, e.want) { |
| t.Errorf("Got edges %v, want %v", edges, e.want) |
| } |
| } |
| } |