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