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
27
28
29
30 package types2_test
31
32 import (
33 "bytes"
34 "cmd/compile/internal/syntax"
35 "flag"
36 "fmt"
37 "internal/buildcfg"
38 "internal/testenv"
39 "os"
40 "path/filepath"
41 "reflect"
42 "regexp"
43 "runtime"
44 "strconv"
45 "strings"
46 "testing"
47
48 . "cmd/compile/internal/types2"
49 )
50
51 var (
52 haltOnError = flag.Bool("halt", false, "halt on error")
53 verifyErrors = flag.Bool("verify", false, "verify errors (rather than list them) in TestManual")
54 )
55
56 func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode syntax.Mode) ([]*syntax.File, []error) {
57 var files []*syntax.File
58 var errlist []error
59 errh := func(err error) { errlist = append(errlist, err) }
60 for i, filename := range filenames {
61 base := syntax.NewFileBase(filename)
62 r := bytes.NewReader(srcs[i])
63 file, err := syntax.Parse(base, r, errh, nil, mode)
64 if file == nil {
65 t.Fatalf("%s: %s", filename, err)
66 }
67 files = append(files, file)
68 }
69 return files, errlist
70 }
71
72 func unpackError(err error) (syntax.Pos, string) {
73 switch err := err.(type) {
74 case syntax.Error:
75 return err.Pos, err.Msg
76 case Error:
77 return err.Pos, err.Msg
78 default:
79 return nopos, err.Error()
80 }
81 }
82
83
84 func absDiff(x, y uint) uint {
85 if x < y {
86 return y - x
87 }
88 return x - y
89 }
90
91
92
93
94 func parseFlags(src []byte, flags *flag.FlagSet) error {
95
96 const prefix = "//"
97 if !bytes.HasPrefix(src, []byte(prefix)) {
98 return nil
99 }
100 src = src[len(prefix):]
101 if i := bytes.Index(src, []byte("-")); i < 0 || len(bytes.TrimSpace(src[:i])) != 0 {
102 return nil
103 }
104 end := bytes.Index(src, []byte("\n"))
105 const maxLen = 256
106 if end < 0 || end > maxLen {
107 return fmt.Errorf("flags comment line too long")
108 }
109
110 return flags.Parse(strings.Fields(string(src[:end])))
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 func testFiles(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, manual bool, opts ...func(*Config)) {
125 enableAlias := true
126 opts = append(opts, func(conf *Config) { conf.EnableAlias = enableAlias })
127 testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
128 if !manual {
129 enableAlias = false
130 testFilesImpl(t, filenames, srcs, colDelta, manual, opts...)
131 }
132 }
133
134 func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, colDelta uint, manual bool, opts ...func(*Config)) {
135 if len(filenames) == 0 {
136 t.Fatal("no source files")
137 }
138
139
140 files, errlist := parseFiles(t, filenames, srcs, 0)
141 pkgName := "<no package>"
142 if len(files) > 0 {
143 pkgName = files[0].PkgName.Value
144 }
145 listErrors := manual && !*verifyErrors
146 if listErrors && len(errlist) > 0 {
147 t.Errorf("--- %s:", pkgName)
148 for _, err := range errlist {
149 t.Error(err)
150 }
151 }
152
153
154 var conf Config
155 conf.Trace = manual && testing.Verbose()
156 conf.Importer = defaultImporter()
157 conf.Error = func(err error) {
158 if *haltOnError {
159 defer panic(err)
160 }
161 if listErrors {
162 t.Error(err)
163 return
164 }
165 errlist = append(errlist, err)
166 }
167
168
169 for _, opt := range opts {
170 opt(&conf)
171 }
172
173
174 var goexperiment, gotypesalias string
175 flags := flag.NewFlagSet("", flag.PanicOnError)
176 flags.StringVar(&conf.GoVersion, "lang", "", "")
177 flags.StringVar(&goexperiment, "goexperiment", "", "")
178 flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
179 flags.StringVar(&gotypesalias, "gotypesalias", "", "")
180 if err := parseFlags(srcs[0], flags); err != nil {
181 t.Fatal(err)
182 }
183
184 exp, err := buildcfg.ParseGOEXPERIMENT(runtime.GOOS, runtime.GOARCH, goexperiment)
185 if err != nil {
186 t.Fatal(err)
187 }
188 old := buildcfg.Experiment
189 defer func() {
190 buildcfg.Experiment = old
191 }()
192 buildcfg.Experiment = *exp
193
194
195 if gotypesalias != "" {
196 conf.EnableAlias = gotypesalias != "0"
197 }
198
199
200 info := Info{
201 Types: make(map[syntax.Expr]TypeAndValue),
202 Instances: make(map[*syntax.Name]Instance),
203 Defs: make(map[*syntax.Name]Object),
204 Uses: make(map[*syntax.Name]Object),
205 Implicits: make(map[syntax.Node]Object),
206 Selections: make(map[*syntax.SelectorExpr]*Selection),
207 Scopes: make(map[syntax.Node]*Scope),
208 FileVersions: make(map[*syntax.PosBase]string),
209 }
210
211
212 conf.Check(pkgName, files, &info)
213 if listErrors {
214 return
215 }
216
217
218 errmap := make(map[string]map[uint][]syntax.Error)
219 for i, filename := range filenames {
220 if m := syntax.CommentMap(bytes.NewReader(srcs[i]), regexp.MustCompile("^ ERRORx? ")); len(m) > 0 {
221 errmap[filename] = m
222 }
223 }
224
225
226 var indices []int
227 for _, err := range errlist {
228 gotPos, gotMsg := unpackError(err)
229
230
231 filename := gotPos.Base().Filename()
232 filemap := errmap[filename]
233 line := gotPos.Line()
234 var errList []syntax.Error
235 if filemap != nil {
236 errList = filemap[line]
237 }
238
239
240 indices = indices[:0]
241 for i, want := range errList {
242 pattern, substr := strings.CutPrefix(want.Msg, " ERROR ")
243 if !substr {
244 var found bool
245 pattern, found = strings.CutPrefix(want.Msg, " ERRORx ")
246 if !found {
247 panic("unreachable")
248 }
249 }
250 unquoted, err := strconv.Unquote(strings.TrimSpace(pattern))
251 if err != nil {
252 t.Errorf("%s:%d:%d: invalid ERROR pattern (cannot unquote %s)", filename, line, want.Pos.Col(), pattern)
253 continue
254 }
255 if substr {
256 if !strings.Contains(gotMsg, unquoted) {
257 continue
258 }
259 } else {
260 rx, err := regexp.Compile(unquoted)
261 if err != nil {
262 t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
263 continue
264 }
265 if !rx.MatchString(gotMsg) {
266 continue
267 }
268 }
269 indices = append(indices, i)
270 }
271 if len(indices) == 0 {
272 t.Errorf("%s: no error expected: %q", gotPos, gotMsg)
273 continue
274 }
275
276
277
278 index := -1
279 var delta uint
280 for _, i := range indices {
281 if d := absDiff(gotPos.Col(), errList[i].Pos.Col()); index < 0 || d < delta {
282 index, delta = i, d
283 }
284 }
285
286
287 if delta > colDelta {
288 t.Errorf("%s: got col = %d; want %d", gotPos, gotPos.Col(), errList[index].Pos.Col())
289 }
290
291
292 if n := len(errList) - 1; n > 0 {
293
294 copy(errList[index:], errList[index+1:])
295 filemap[line] = errList[:n]
296 } else {
297
298 delete(filemap, line)
299 }
300
301
302 if len(filemap) == 0 {
303 delete(errmap, filename)
304 }
305 }
306
307
308 if len(errmap) > 0 {
309 t.Errorf("--- %s: unreported errors:", pkgName)
310 for filename, filemap := range errmap {
311 for line, errList := range filemap {
312 for _, err := range errList {
313 t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
314 }
315 }
316 }
317 }
318 }
319
320
321
322 func boolFieldAddr(conf *Config, name string) *bool {
323 v := reflect.Indirect(reflect.ValueOf(conf))
324 return (*bool)(v.FieldByName(name).Addr().UnsafePointer())
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 func TestManual(t *testing.T) {
342 testenv.MustHaveGoBuild(t)
343
344 filenames := flag.Args()
345 if len(filenames) == 0 {
346 filenames = []string{filepath.FromSlash("testdata/manual.go")}
347 }
348
349 info, err := os.Stat(filenames[0])
350 if err != nil {
351 t.Fatalf("TestManual: %v", err)
352 }
353
354 DefPredeclaredTestFuncs()
355 if info.IsDir() {
356 if len(filenames) > 1 {
357 t.Fatal("TestManual: must have only one directory argument")
358 }
359 testDir(t, filenames[0], 0, true)
360 } else {
361 testPkg(t, filenames, 0, true)
362 }
363 }
364
365 func TestLongConstants(t *testing.T) {
366 format := `package longconst; const _ = %s /* ERROR "constant overflow" */; const _ = %s // ERROR "excessively long constant"`
367 src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001))
368 testFiles(t, []string{"longconst.go"}, [][]byte{[]byte(src)}, 0, false)
369 }
370
371 func withSizes(sizes Sizes) func(*Config) {
372 return func(cfg *Config) {
373 cfg.Sizes = sizes
374 }
375 }
376
377
378
379
380 func TestIndexRepresentability(t *testing.T) {
381 const src = `package index; var s []byte; var _ = s[int64 /* ERRORx "int64\\(1\\) << 40 \\(.*\\) overflows int" */ (1) << 40]`
382 testFiles(t, []string{"index.go"}, [][]byte{[]byte(src)}, 0, false, withSizes(&StdSizes{4, 4}))
383 }
384
385 func TestIssue47243_TypedRHS(t *testing.T) {
386
387
388 const src = `package issue47243; var a uint64; var _ = a << uint64(4294967296)`
389 testFiles(t, []string{"p.go"}, [][]byte{[]byte(src)}, 0, false, withSizes(&StdSizes{4, 4}))
390 }
391
392 func TestCheck(t *testing.T) {
393 old := buildcfg.Experiment.RangeFunc
394 defer func() {
395 buildcfg.Experiment.RangeFunc = old
396 }()
397 buildcfg.Experiment.RangeFunc = true
398
399 DefPredeclaredTestFuncs()
400 testDirFiles(t, "../../../../internal/types/testdata/check", 50, false)
401 }
402 func TestSpec(t *testing.T) { testDirFiles(t, "../../../../internal/types/testdata/spec", 20, false) }
403 func TestExamples(t *testing.T) {
404 testDirFiles(t, "../../../../internal/types/testdata/examples", 125, false)
405 }
406 func TestFixedbugs(t *testing.T) {
407 testDirFiles(t, "../../../../internal/types/testdata/fixedbugs", 100, false)
408 }
409 func TestLocal(t *testing.T) { testDirFiles(t, "testdata/local", 0, false) }
410
411 func testDirFiles(t *testing.T, dir string, colDelta uint, manual bool) {
412 testenv.MustHaveGoBuild(t)
413 dir = filepath.FromSlash(dir)
414
415 fis, err := os.ReadDir(dir)
416 if err != nil {
417 t.Error(err)
418 return
419 }
420
421 for _, fi := range fis {
422 path := filepath.Join(dir, fi.Name())
423
424
425 if fi.IsDir() {
426 testDir(t, path, colDelta, manual)
427 } else {
428 t.Run(filepath.Base(path), func(t *testing.T) {
429 testPkg(t, []string{path}, colDelta, manual)
430 })
431 }
432 }
433 }
434
435 func testDir(t *testing.T, dir string, colDelta uint, manual bool) {
436 fis, err := os.ReadDir(dir)
437 if err != nil {
438 t.Error(err)
439 return
440 }
441
442 var filenames []string
443 for _, fi := range fis {
444 filenames = append(filenames, filepath.Join(dir, fi.Name()))
445 }
446
447 t.Run(filepath.Base(dir), func(t *testing.T) {
448 testPkg(t, filenames, colDelta, manual)
449 })
450 }
451
452 func testPkg(t *testing.T, filenames []string, colDelta uint, manual bool) {
453 srcs := make([][]byte, len(filenames))
454 for i, filename := range filenames {
455 src, err := os.ReadFile(filename)
456 if err != nil {
457 t.Fatalf("could not read %s: %v", filename, err)
458 }
459 srcs[i] = src
460 }
461 testFiles(t, filenames, srcs, colDelta, manual)
462 }
463
View as plain text