GoPLS Viewer

Home|gopls/go/types/typeutil/callee_test.go
1// Copyright 2018 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
5package typeutil_test
6
7import (
8    "fmt"
9    "go/ast"
10    "go/parser"
11    "go/token"
12    "go/types"
13    "strings"
14    "testing"
15
16    "golang.org/x/tools/go/types/typeutil"
17    "golang.org/x/tools/internal/typeparams"
18)
19
20func TestStaticCallee(t *testing.T) {
21    testStaticCallee(t, []string{
22        `package q;
23        func Abs(x int) int {
24            if x < 0 {
25                return -x
26            }
27            return x
28        }`,
29        `package p
30        import "q"
31
32        type T int
33
34        func g(int)
35
36        var f = g
37
38        var x int
39
40        type s struct{ f func(int) }
41        func (s) g(int)
42
43        type I interface{ f(int) }
44
45        var a struct{b struct{c s}}
46
47        var n map[int]func()
48        var m []func()
49
50        func calls() {
51            g(x)           // a declared func
52            s{}.g(x)       // a concrete method
53            a.b.c.g(x)     // same
54            _ = q.Abs(x)   // declared func, qualified identifier
55        }
56
57        func noncalls() {
58            _ = T(x)    // a type
59            f(x)        // a var
60            panic(x)    // a built-in
61            s{}.f(x)    // a field
62            I(nil).f(x) // interface method
63            m[0]()      // a map
64            n[0]()      // a slice
65        }
66        `})
67}
68
69func TestTypeParamStaticCallee(t *testing.T) {
70    if !typeparams.Enabled {
71        t.Skip("type parameters are not enabled")
72    }
73    testStaticCallee(t, []string{
74        `package q
75        func R[T any]() {}
76        `,
77        `package p
78        import "q"
79        type I interface{
80            i()
81        }
82
83        type G[T any] func() T
84        func F[T any]() T { var x T; return x }
85
86        type M[T I] struct{ t T }
87        func (m M[T]) noncalls() {
88            m.t.i()   // method on a type parameter
89        }
90
91        func (m M[T]) calls() {
92            m.calls() // method on a generic type
93        }
94
95        type Chain[T I] struct{ r struct { s M[T] } }
96
97        type S int
98        func (S) i() {}
99
100        func Multi[TP0, TP1 any](){}
101
102        func calls() {
103            _ = F[int]()            // instantiated function
104            _ = (F[int])()          // go through parens
105            M[S]{}.calls()          // instantiated method
106            Chain[S]{}.r.s.calls()  // same as above
107            Multi[int,string]()     // multiple type parameters
108            q.R[int]()              // different package
109        }
110
111        func noncalls() {
112            _ = G[int](nil)()  // instantiated function
113        }
114        `})
115}
116
117// testStaticCallee parses and type checks each file content in contents
118// as a single file package in order. Within functions that have the suffix
119// "calls" it checks that the CallExprs within have a static callee.
120// If the function's name == "calls" all calls must have static callees,
121// and if the name != "calls", the calls must not have static callees.
122// Failures are reported on t.
123func testStaticCallee(t *testing.Tcontents []string) {
124    fset := token.NewFileSet()
125    packages := make(map[string]*types.Package)
126    cfg := &types.Config{Importerclosure(packages)}
127    info := &types.Info{
128        Uses:       make(map[*ast.Ident]types.Object),
129        Selectionsmake(map[*ast.SelectorExpr]*types.Selection),
130    }
131    typeparams.InitInstanceInfo(info)
132
133    var files []*ast.File
134    for icontent := range contents {
135        // parse
136        ferr := parser.ParseFile(fsetfmt.Sprintf("%d.go"i), content0)
137        if err != nil {
138            t.Fatal(err)
139        }
140        files = append(filesf)
141
142        // type-check
143        pkgerr := cfg.Check(f.Name.Namefset, []*ast.File{f}, info)
144        if err != nil {
145            t.Fatal(err)
146        }
147        packages[pkg.Path()] = pkg
148    }
149
150    // check
151    for _f := range files {
152        for _decl := range f.Decls {
153            if declok := decl.(*ast.FuncDecl); ok && strings.HasSuffix(decl.Name.Name"calls") {
154                wantCallee := decl.Name.Name == "calls" // false within func noncalls()
155                ast.Inspect(decl.Body, func(n ast.Nodebool {
156                    if callok := n.(*ast.CallExpr); ok {
157                        fn := typeutil.StaticCallee(infocall)
158                        if fn == nil && wantCallee {
159                            t.Errorf("%s: StaticCallee returned nil",
160                                fset.Position(call.Lparen))
161                        } else if fn != nil && !wantCallee {
162                            t.Errorf("%s: StaticCallee returned %s, want nil",
163                                fset.Position(call.Lparen), fn)
164                        }
165                    }
166                    return true
167                })
168            }
169        }
170    }
171}
172
MembersX
testStaticCallee
testStaticCallee.fset
testStaticCallee.packages
testStaticCallee.files
testStaticCallee.RangeStmt_2923.BlockStmt.f
strings
TestStaticCallee
TestStaticCallee.t
testStaticCallee.RangeStmt_3270.f
testStaticCallee.cfg
testStaticCallee.RangeStmt_2923.BlockStmt.err
testStaticCallee.RangeStmt_2923.BlockStmt.pkg
parser
token
typeutil
testStaticCallee.contents
testStaticCallee.info
testStaticCallee.RangeStmt_2923.i
testStaticCallee.RangeStmt_2923.content
fmt
testing
TestTypeParamStaticCallee
testStaticCallee.RangeStmt_3270.BlockStmt.RangeStmt_3298.BlockStmt.BlockStmt.BlockStmt.BlockStmt.fn
TestTypeParamStaticCallee.t
testStaticCallee.t
testStaticCallee.RangeStmt_3270.BlockStmt.RangeStmt_3298.decl
Members
X