| 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