1 | // Copyright 2022 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 ssa_test |
6 | |
7 | import ( |
8 | "go/ast" |
9 | "go/parser" |
10 | "go/token" |
11 | "go/types" |
12 | "testing" |
13 | |
14 | "golang.org/x/tools/go/ssa" |
15 | "golang.org/x/tools/go/ssa/ssautil" |
16 | "golang.org/x/tools/internal/typeparams" |
17 | ) |
18 | |
19 | // Tests that MethodValue returns the expected method. |
20 | func TestMethodValue(t *testing.T) { |
21 | if !typeparams.Enabled { |
22 | t.Skip("TestMethodValue requires type parameters") |
23 | } |
24 | input := ` |
25 | package p |
26 | |
27 | type I interface{ M() } |
28 | |
29 | type S int |
30 | func (S) M() {} |
31 | type R[T any] struct{ S } |
32 | |
33 | var i I |
34 | var s S |
35 | var r R[string] |
36 | |
37 | func selections[T any]() { |
38 | _ = i.M |
39 | _ = s.M |
40 | _ = r.M |
41 | |
42 | var v R[T] |
43 | _ = v.M |
44 | } |
45 | ` |
46 | |
47 | // Parse the file. |
48 | fset := token.NewFileSet() |
49 | f, err := parser.ParseFile(fset, "input.go", input, 0) |
50 | if err != nil { |
51 | t.Error(err) |
52 | return |
53 | } |
54 | |
55 | // Build an SSA program from the parsed file. |
56 | p, info, err := ssautil.BuildPackage(&types.Config{}, fset, |
57 | types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions) |
58 | if err != nil { |
59 | t.Error(err) |
60 | return |
61 | } |
62 | |
63 | // Collect all of the *types.Selection in the function "selections". |
64 | var selections []*types.Selection |
65 | for _, decl := range f.Decls { |
66 | if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == "selections" { |
67 | for _, stmt := range fn.Body.List { |
68 | if assign, ok := stmt.(*ast.AssignStmt); ok { |
69 | sel := assign.Rhs[0].(*ast.SelectorExpr) |
70 | selections = append(selections, info.Selections[sel]) |
71 | } |
72 | } |
73 | } |
74 | } |
75 | |
76 | wants := map[string]string{ |
77 | "method (p.S) M()": "(p.S).M", |
78 | "method (p.R[string]) M()": "(p.R[string]).M", |
79 | "method (p.I) M()": "nil", // interface |
80 | "method (p.R[T]) M()": "nil", // parameterized |
81 | } |
82 | if len(wants) != len(selections) { |
83 | t.Fatalf("Wanted %d selections. got %d", len(wants), len(selections)) |
84 | } |
85 | for _, selection := range selections { |
86 | var got string |
87 | if m := p.Prog.MethodValue(selection); m != nil { |
88 | got = m.String() |
89 | } else { |
90 | got = "nil" |
91 | } |
92 | if want := wants[selection.String()]; want != got { |
93 | t.Errorf("p.Prog.MethodValue(%s) expected %q. got %q", selection, want, got) |
94 | } |
95 | } |
96 | } |
97 |
Members