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