Source file
src/go/types/check_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package types_test
24
25 import (
26 "flag"
27 "fmt"
28 "go/ast"
29 "go/importer"
30 "go/internal/typeparams"
31 "go/parser"
32 "go/scanner"
33 "go/token"
34 "internal/buildcfg"
35 "internal/testenv"
36 "os"
37 "path/filepath"
38 "regexp"
39 "strings"
40 "testing"
41
42 . "go/types"
43 )
44
45 var (
46 haltOnError = flag.Bool("halt", false, "halt on error")
47 verifyErrors = flag.Bool("verify", false, "verify errors (rather than list them) in TestManual")
48 goVersion = flag.String("lang", "", "Go language version (e.g. \"go1.12\") for TestManual")
49 )
50
51 var fset = token.NewFileSet()
52
53
54 var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(?s)(.*)`)
55
56
57
58
59
60 func splitError(err error) (pos, msg string) {
61 msg = err.Error()
62 if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
63 pos = m[1]
64 msg = m[2]
65 }
66 return
67 }
68
69 func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode parser.Mode) ([]*ast.File, []error) {
70 var files []*ast.File
71 var errlist []error
72 for i, filename := range filenames {
73 file, err := parser.ParseFile(fset, filename, srcs[i], mode)
74 if file == nil {
75 t.Fatalf("%s: %s", filename, err)
76 }
77 files = append(files, file)
78 if err != nil {
79 if list, _ := err.(scanner.ErrorList); len(list) > 0 {
80 for _, err := range list {
81 errlist = append(errlist, err)
82 }
83 } else {
84 errlist = append(errlist, err)
85 }
86 }
87 }
88 return files, errlist
89 }
90
91
92
93
94
95
96
97 var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
98
99
100
101
102
103
104 func errMap(t *testing.T, files []*ast.File, srcs [][]byte) map[string][]string {
105
106 errmap := make(map[string][]string)
107
108 for i, file := range files {
109 tok := fset.File(file.Package)
110 src := srcs[i]
111 var s scanner.Scanner
112 s.Init(tok, src, nil, scanner.ScanComments)
113 var prev token.Pos
114 var here token.Pos
115
116 scanFile:
117 for {
118 pos, tok, lit := s.Scan()
119 switch tok {
120 case token.EOF:
121 break scanFile
122 case token.COMMENT:
123 if lit[1] == '*' {
124 lit = lit[:len(lit)-2]
125 }
126 if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
127 pos := prev
128 if s[1] == "HERE" {
129 pos = here
130 }
131 p := fset.Position(pos).String()
132 errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
133 }
134 case token.SEMICOLON:
135
136 if lit == "\n" {
137 continue scanFile
138 }
139 fallthrough
140 default:
141 prev = pos
142 var l int
143 if tok.IsLiteral() {
144 l = len(lit)
145 } else {
146 l = len(tok.String())
147 }
148 here = prev + token.Pos(l)
149 }
150 }
151 }
152
153 return errmap
154 }
155
156 func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
157 for _, err := range errlist {
158 pos, gotMsg := splitError(err)
159 list := errmap[pos]
160 index := -1
161
162 for i, wantRx := range list {
163 rx, err := regexp.Compile(wantRx)
164 if err != nil {
165 t.Errorf("%s: %v", pos, err)
166 continue
167 }
168 if rx.MatchString(gotMsg) {
169 index = i
170 break
171 }
172 }
173 if index >= 0 {
174
175 if n := len(list) - 1; n > 0 {
176
177 list[index] = list[n]
178 errmap[pos] = list[:n]
179 } else {
180
181 delete(errmap, pos)
182 }
183 } else {
184 t.Errorf("%s: no error expected: %q", pos, gotMsg)
185 }
186 }
187 }
188
189
190 var goVersionRx = regexp.MustCompile(`^go[1-9][0-9]*_(0|[1-9][0-9]*)$`)
191
192
193
194
195
196 func asGoVersion(s string) string {
197 if goVersionRx.MatchString(s) {
198 return strings.Replace(s, "_", ".", 1)
199 }
200 return ""
201 }
202
203
204
205
206 var excludedForUnifiedBuild = map[string]bool{
207 "issue47818.go2": true,
208 "issue49705.go2": true,
209 }
210
211 func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, manual bool, imp Importer) {
212 if len(filenames) == 0 {
213 t.Fatal("no source files")
214 }
215
216 if buildcfg.Experiment.Unified {
217 for _, f := range filenames {
218 if excludedForUnifiedBuild[filepath.Base(f)] {
219 t.Logf("%s cannot be tested with unified build - skipped", f)
220 return
221 }
222 }
223 }
224
225 if strings.HasSuffix(filenames[0], ".go1") {
226
227 t.Skip("type params are enabled")
228 }
229
230 mode := parser.AllErrors
231 if !strings.HasSuffix(filenames[0], ".go2") && !manual {
232 mode |= typeparams.DisallowParsing
233 }
234
235
236 files, errlist := parseFiles(t, filenames, srcs, mode)
237
238 pkgName := "<no package>"
239 if len(files) > 0 {
240 pkgName = files[0].Name.Name
241 }
242
243
244 goVersion := *goVersion
245 if goVersion == "" {
246 goVersion = asGoVersion(pkgName)
247 }
248
249 listErrors := manual && !*verifyErrors
250 if listErrors && len(errlist) > 0 {
251 t.Errorf("--- %s:", pkgName)
252 for _, err := range errlist {
253 t.Error(err)
254 }
255 }
256
257
258 var conf Config
259 conf.Sizes = sizes
260 conf.GoVersion = goVersion
261
262
263 if len(filenames) == 1 {
264 if strings.HasSuffix(filenames[0], "importC.src") {
265 conf.FakeImportC = true
266 }
267 }
268
269 conf.Importer = imp
270 if imp == nil {
271 conf.Importer = importer.Default()
272 }
273 conf.Error = func(err error) {
274 if *haltOnError {
275 defer panic(err)
276 }
277 if listErrors {
278 t.Error(err)
279 return
280 }
281
282
283 if !strings.Contains(err.Error(), ": \t") {
284 errlist = append(errlist, err)
285 }
286 }
287 conf.Check(pkgName, fset, files, nil)
288
289 if listErrors {
290 return
291 }
292
293 for _, err := range errlist {
294 err, ok := err.(Error)
295 if !ok {
296 continue
297 }
298 code := readCode(err)
299 if code == 0 {
300 t.Errorf("missing error code: %v", err)
301 }
302 }
303
304
305
306 errmap := errMap(t, files, srcs)
307 eliminate(t, errmap, errlist)
308
309
310 if len(errmap) > 0 {
311 t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
312 for pos, list := range errmap {
313 for _, rx := range list {
314 t.Errorf("%s: %q", pos, rx)
315 }
316 }
317 }
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334 func TestManual(t *testing.T) {
335 testenv.MustHaveGoBuild(t)
336
337 filenames := flag.Args()
338 if len(filenames) == 0 {
339 filenames = []string{filepath.FromSlash("testdata/manual.go2")}
340 }
341
342 info, err := os.Stat(filenames[0])
343 if err != nil {
344 t.Fatalf("TestManual: %v", err)
345 }
346
347 DefPredeclaredTestFuncs()
348 if info.IsDir() {
349 if len(filenames) > 1 {
350 t.Fatal("TestManual: must have only one directory argument")
351 }
352 testDir(t, filenames[0], true)
353 } else {
354 testPkg(t, filenames, true)
355 }
356 }
357
358 func TestLongConstants(t *testing.T) {
359 format := "package longconst\n\nconst _ = %s\nconst _ = %s // ERROR excessively long constant"
360 src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001))
361 testFiles(t, nil, []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil)
362 }
363
364
365
366
367 func TestIndexRepresentability(t *testing.T) {
368 const src = "package index\n\nvar s []byte\nvar _ = s[int64 /* ERROR \"int64\\(1\\) << 40 \\(.*\\) overflows int\" */ (1) << 40]"
369 testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false, nil)
370 }
371
372 func TestIssue47243_TypedRHS(t *testing.T) {
373
374
375 const src = "package issue47243\n\nvar a uint64; var _ = a << uint64(4294967296)"
376 testFiles(t, &StdSizes{4, 4}, []string{"p.go"}, [][]byte{[]byte(src)}, false, nil)
377 }
378
379 func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
380 func TestSpec(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) }
381 func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) }
382 func TestFixedbugs(t *testing.T) {
383 DefPredeclaredTestFuncs()
384 testDirFiles(t, "testdata/fixedbugs", false)
385 }
386
387 func testDirFiles(t *testing.T, dir string, manual bool) {
388 testenv.MustHaveGoBuild(t)
389 dir = filepath.FromSlash(dir)
390
391 fis, err := os.ReadDir(dir)
392 if err != nil {
393 t.Error(err)
394 return
395 }
396
397 for _, fi := range fis {
398 path := filepath.Join(dir, fi.Name())
399
400
401 if fi.IsDir() {
402 testDir(t, path, manual)
403 } else {
404 t.Run(filepath.Base(path), func(t *testing.T) {
405 testPkg(t, []string{path}, manual)
406 })
407 }
408 }
409 }
410
411 func testDir(t *testing.T, dir string, manual bool) {
412 testenv.MustHaveGoBuild(t)
413
414 fis, err := os.ReadDir(dir)
415 if err != nil {
416 t.Error(err)
417 return
418 }
419
420 var filenames []string
421 for _, fi := range fis {
422 filenames = append(filenames, filepath.Join(dir, fi.Name()))
423 }
424
425 t.Run(filepath.Base(dir), func(t *testing.T) {
426 testPkg(t, filenames, manual)
427 })
428 }
429
430
431 func testPkg(t *testing.T, filenames []string, manual bool) {
432 srcs := make([][]byte, len(filenames))
433 for i, filename := range filenames {
434 src, err := os.ReadFile(filename)
435 if err != nil {
436 t.Fatalf("could not read %s: %v", filename, err)
437 }
438 srcs[i] = src
439 }
440 testFiles(t, nil, filenames, srcs, manual, nil)
441 }
442
View as plain text