Source file
src/go/types/eval_test.go
1
2
3
4
5
6
7 package types_test
8
9 import (
10 "fmt"
11 "go/ast"
12 "go/importer"
13 "go/parser"
14 "go/token"
15 "go/types"
16 "internal/godebug"
17 "internal/testenv"
18 "strings"
19 "testing"
20
21 . "go/types"
22 )
23
24 func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) {
25 gotTv, err := Eval(fset, pkg, pos, expr)
26 if err != nil {
27 t.Errorf("Eval(%q) failed: %s", expr, err)
28 return
29 }
30 if gotTv.Type == nil {
31 t.Errorf("Eval(%q) got nil type but no error", expr)
32 return
33 }
34
35
36 if typ != nil {
37
38 if !Identical(gotTv.Type, typ) {
39 t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ)
40 return
41 }
42 } else {
43
44 gotStr := gotTv.Type.String()
45 if gotStr != typStr {
46 t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr)
47 return
48 }
49 }
50
51
52 gotStr := ""
53 if gotTv.Value != nil {
54 gotStr = gotTv.Value.ExactString()
55 }
56 if gotStr != valStr {
57 t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
58 }
59 }
60
61 func TestEvalBasic(t *testing.T) {
62 fset := token.NewFileSet()
63 for _, typ := range Typ[Bool : String+1] {
64 testEval(t, fset, nil, nopos, typ.Name(), typ, "", "")
65 }
66 }
67
68 func TestEvalComposite(t *testing.T) {
69 fset := token.NewFileSet()
70 for _, test := range independentTestTypes {
71 testEval(t, fset, nil, nopos, test.src, nil, test.str, "")
72 }
73 }
74
75 func TestEvalArith(t *testing.T) {
76 var tests = []string{
77 `true`,
78 `false == false`,
79 `12345678 + 87654321 == 99999999`,
80 `10 * 20 == 200`,
81 `(1<<500)*2 >> 100 == 2<<400`,
82 `"foo" + "bar" == "foobar"`,
83 `"abc" <= "bcd"`,
84 `len([10]struct{}{}) == 2*5`,
85 }
86 fset := token.NewFileSet()
87 for _, test := range tests {
88 testEval(t, fset, nil, nopos, test, Typ[UntypedBool], "", "true")
89 }
90 }
91
92 func TestEvalPos(t *testing.T) {
93 testenv.MustHaveGoBuild(t)
94
95
96
97
98
99
100
101 var sources = []string{
102 `
103 package p
104 import "fmt"
105 import m "math"
106 const c = 3.0
107 type T []int
108 func f(a int, s string) float64 {
109 fmt.Println("calling f")
110 _ = m.Pi // use package math
111 const d int = c + 1
112 var x int
113 x = a + len(s)
114 return float64(x)
115 /* true => true, untyped bool */
116 /* fmt.Println => , func(a ...any) (n int, err error) */
117 /* c => 3, untyped float */
118 /* T => , p.T */
119 /* a => , int */
120 /* s => , string */
121 /* d => 4, int */
122 /* x => , int */
123 /* d/c => 1, int */
124 /* c/2 => 3/2, untyped float */
125 /* m.Pi < m.E => false, untyped bool */
126 }
127 `,
128 `
129 package p
130 /* c => 3, untyped float */
131 type T1 /* T1 => , p.T1 */ struct {}
132 var v1 /* v1 => , int */ = 42
133 func /* f1 => , func(v1 float64) */ f1(v1 float64) {
134 /* f1 => , func(v1 float64) */
135 /* v1 => , float64 */
136 var c /* c => 3, untyped float */ = "foo" /* c => , string */
137 {
138 var c struct {
139 c /* c => , string */ int
140 }
141 /* c => , struct{c int} */
142 _ = c
143 }
144 _ = func(a, b, c int /* c => , string */) /* c => , int */ {
145 /* c => , int */
146 }
147 _ = c
148 type FT /* FT => , p.FT */ interface{}
149 }
150 `,
151 `
152 package p
153 /* T => , p.T */
154 `,
155 `
156 package p
157 import "io"
158 type R = io.Reader
159 func _() {
160 /* interface{R}.Read => , func(_ interface{io.Reader}, p []byte) (n int, err error) */
161 _ = func() {
162 /* interface{io.Writer}.Write => , func(_ interface{io.Writer}, p []byte) (n int, err error) */
163 type io interface {} // must not shadow io in line above
164 }
165 type R interface {} // must not shadow R in first line of this function body
166 }
167 `,
168 }
169
170 fset := token.NewFileSet()
171 var files []*ast.File
172 for i, src := range sources {
173 file, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
174 if err != nil {
175 t.Fatalf("could not parse file %d: %s", i, err)
176 }
177
178
179
180
181 switch gotypesalias.Value() {
182 case "", "1":
183 if strings.Contains(src, "interface{R}.Read") {
184 continue
185 }
186 }
187
188 files = append(files, file)
189 }
190
191 conf := Config{Importer: importer.Default()}
192 pkg, err := conf.Check("p", fset, files, nil)
193 if err != nil {
194 t.Fatal(err)
195 }
196
197 for _, file := range files {
198 for _, group := range file.Comments {
199 for _, comment := range group.List {
200 s := comment.Text
201 if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" {
202 str, typ := split(s[2:len(s)-2], ", ")
203 str, val := split(str, "=>")
204 testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val)
205 }
206 }
207 }
208 }
209 }
210
211
212 var gotypesalias = godebug.New("#gotypesalias")
213
214
215 func split(s, sep string) (string, string) {
216 before, after, _ := strings.Cut(s, sep)
217 return strings.TrimSpace(before), strings.TrimSpace(after)
218 }
219
220 func TestCheckExpr(t *testing.T) {
221 testenv.MustHaveGoBuild(t)
222
223
224
225
226
227 const src = `
228 package p
229
230 import "fmt"
231
232 const c = 3.0
233 type T []int
234 type S struct{ X int }
235
236 func f(a int, s string) S {
237 /* fmt.Println => func fmt.Println(a ...any) (n int, err error) */
238 /* fmt.Stringer.String => func (fmt.Stringer).String() string */
239 fmt.Println("calling f")
240
241 var fmt struct{ Println int }
242 /* fmt => var fmt struct{Println int} */
243 /* fmt.Println => field Println int */
244 /* f(1, "").X => field X int */
245 fmt.Println = 1
246
247 /* append => builtin append */
248
249 /* new(S).X => field X int */
250
251 return S{}
252 }`
253
254 fset := token.NewFileSet()
255 f, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
256 if err != nil {
257 t.Fatal(err)
258 }
259
260 conf := Config{Importer: importer.Default()}
261 pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
262 if err != nil {
263 t.Fatal(err)
264 }
265
266 checkExpr := func(pos token.Pos, str string) (Object, error) {
267 expr, err := parser.ParseExprFrom(fset, "eval", str, 0)
268 if err != nil {
269 return nil, err
270 }
271
272 info := &Info{
273 Uses: make(map[*ast.Ident]Object),
274 Selections: make(map[*ast.SelectorExpr]*Selection),
275 }
276 if err := CheckExpr(fset, pkg, pos, expr, info); err != nil {
277 return nil, fmt.Errorf("CheckExpr(%q) failed: %s", str, err)
278 }
279 switch expr := expr.(type) {
280 case *ast.Ident:
281 if obj, ok := info.Uses[expr]; ok {
282 return obj, nil
283 }
284 case *ast.SelectorExpr:
285 if sel, ok := info.Selections[expr]; ok {
286 return sel.Obj(), nil
287 }
288 if obj, ok := info.Uses[expr.Sel]; ok {
289 return obj, nil
290 }
291 }
292 return nil, fmt.Errorf("no object for %s", str)
293 }
294
295 for _, group := range f.Comments {
296 for _, comment := range group.List {
297 s := comment.Text
298 if len(s) >= 4 && strings.HasPrefix(s, "/*") && strings.HasSuffix(s, "*/") {
299 pos := comment.Pos()
300 expr, wantObj := split(s[2:len(s)-2], "=>")
301 obj, err := checkExpr(pos, expr)
302 if err != nil {
303 t.Errorf("%s: %s", fset.Position(pos), err)
304 continue
305 }
306 if obj.String() != wantObj {
307 t.Errorf("%s: checkExpr(%s) = %s, want %v",
308 fset.Position(pos), expr, obj, wantObj)
309 }
310 }
311 }
312 }
313 }
314
315 func TestIssue65898(t *testing.T) {
316 const src = `
317 package p
318 func _[A any](A) {}
319 `
320
321 fset := token.NewFileSet()
322 f := mustParse(fset, src)
323
324 var conf types.Config
325 pkg, err := conf.Check(pkgName(src), fset, []*ast.File{f}, nil)
326 if err != nil {
327 t.Fatal(err)
328 }
329
330 for _, d := range f.Decls {
331 if fun, _ := d.(*ast.FuncDecl); fun != nil {
332
333 if err := types.CheckExpr(fset, pkg, fun.Type.Pos(), fun.Type, nil); err == nil || !strings.Contains(err.Error(), "undefined") {
334 t.Fatalf("got %s, want undefined error", err)
335 }
336
337 if err := types.CheckExpr(fset, pkg, fun.Type.End(), fun.Type, nil); err != nil {
338 t.Fatal(err)
339 }
340 }
341 }
342 }
343
View as plain text