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