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 if goexperiment != "" {
185 revert := setGOEXPERIMENT(goexperiment)
186 defer revert()
187 }
188
189
190 if gotypesalias != "" {
191 conf.EnableAlias = gotypesalias != "0"
192 }
193
194
195 info := Info{
196 Types: make(map[syntax.Expr]TypeAndValue),
197 Instances: make(map[*syntax.Name]Instance),
198 Defs: make(map[*syntax.Name]Object),
199 Uses: make(map[*syntax.Name]Object),
200 Implicits: make(map[syntax.Node]Object),
201 Selections: make(map[*syntax.SelectorExpr]*Selection),
202 Scopes: make(map[syntax.Node]*Scope),
203 FileVersions: make(map[*syntax.PosBase]string),
204 }
205
206
207 conf.Check(pkgName, files, &info)
208 if listErrors {
209 return
210 }
211
212
213 errmap := make(map[string]map[uint][]syntax.Error)
214 for i, filename := range filenames {
215 if m := syntax.CommentMap(bytes.NewReader(srcs[i]), regexp.MustCompile("^ ERRORx? ")); len(m) > 0 {
216 errmap[filename] = m
217 }
218 }
219
220
221 var indices []int
222 for _, err := range errlist {
223 gotPos, gotMsg := unpackError(err)
224
225
226 filename := gotPos.Base().Filename()
227 filemap := errmap[filename]
228 line := gotPos.Line()
229 var errList []syntax.Error
230 if filemap != nil {
231 errList = filemap[line]
232 }
233
234
235 indices = indices[:0]
236 for i, want := range errList {
237 pattern, substr := strings.CutPrefix(want.Msg, " ERROR ")
238 if !substr {
239 var found bool
240 pattern, found = strings.CutPrefix(want.Msg, " ERRORx ")
241 if !found {
242 panic("unreachable")
243 }
244 }
245 unquoted, err := strconv.Unquote(strings.TrimSpace(pattern))
246 if err != nil {
247 t.Errorf("%s:%d:%d: invalid ERROR pattern (cannot unquote %s)", filename, line, want.Pos.Col(), pattern)
248 continue
249 }
250 if substr {
251 if !strings.Contains(gotMsg, unquoted) {
252 continue
253 }
254 } else {
255 rx, err := regexp.Compile(unquoted)
256 if err != nil {
257 t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
258 continue
259 }
260 if !rx.MatchString(gotMsg) {
261 continue
262 }
263 }
264 indices = append(indices, i)
265 }
266 if len(indices) == 0 {
267 t.Errorf("%s: no error expected: %q", gotPos, gotMsg)
268 continue
269 }
270
271
272
273 index := -1
274 var delta uint
275 for _, i := range indices {
276 if d := absDiff(gotPos.Col(), errList[i].Pos.Col()); index < 0 || d < delta {
277 index, delta = i, d
278 }
279 }
280
281
282 if delta > colDelta {
283 t.Errorf("%s: got col = %d; want %d", gotPos, gotPos.Col(), errList[index].Pos.Col())
284 }
285
286
287 if n := len(errList) - 1; n > 0 {
288
289 copy(errList[index:], errList[index+1:])
290 filemap[line] = errList[:n]
291 } else {
292
293 delete(filemap, line)
294 }
295
296
297 if len(filemap) == 0 {
298 delete(errmap, filename)
299 }
300 }
301
302
303 if len(errmap) > 0 {
304 t.Errorf("--- %s: unreported errors:", pkgName)
305 for filename, filemap := range errmap {
306 for line, errList := range filemap {
307 for _, err := range errList {
308 t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
309 }
310 }
311 }
312 }
313 }
314
315
316
317 func boolFieldAddr(conf *Config, name string) *bool {
318 v := reflect.Indirect(reflect.ValueOf(conf))
319 return (*bool)(v.FieldByName(name).Addr().UnsafePointer())
320 }
321
322
323
324
325
326 func setGOEXPERIMENT(goexperiment string) func() {
327 exp, err := buildcfg.ParseGOEXPERIMENT(runtime.GOOS, runtime.GOARCH, goexperiment)
328 if err != nil {
329 panic(err)
330 }
331 old := buildcfg.Experiment
332 buildcfg.Experiment = *exp
333 return func() { buildcfg.Experiment = old }
334 }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 func TestManual(t *testing.T) {
351 testenv.MustHaveGoBuild(t)
352
353 filenames := flag.Args()
354 if len(filenames) == 0 {
355 filenames = []string{filepath.FromSlash("testdata/manual.go")}
356 }
357
358 info, err := os.Stat(filenames[0])
359 if err != nil {
360 t.Fatalf("TestManual: %v", err)
361 }
362
363 DefPredeclaredTestFuncs()
364 if info.IsDir() {
365 if len(filenames) > 1 {
366 t.Fatal("TestManual: must have only one directory argument")
367 }
368 testDir(t, filenames[0], 0, true)
369 } else {
370 testPkg(t, filenames, 0, true)
371 }
372 }
373
374 func TestLongConstants(t *testing.T) {
375 format := `package longconst; const _ = %s /* ERROR "constant overflow" */; const _ = %s // ERROR "excessively long constant"`
376 src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001))
377 testFiles(t, []string{"longconst.go"}, [][]byte{[]byte(src)}, 0, false)
378 }
379
380 func withSizes(sizes Sizes) func(*Config) {
381 return func(cfg *Config) {
382 cfg.Sizes = sizes
383 }
384 }
385
386
387
388
389 func TestIndexRepresentability(t *testing.T) {
390 const src = `package index; var s []byte; var _ = s[int64 /* ERRORx "int64\\(1\\) << 40 \\(.*\\) overflows int" */ (1) << 40]`
391 testFiles(t, []string{"index.go"}, [][]byte{[]byte(src)}, 0, false, withSizes(&StdSizes{4, 4}))
392 }
393
394 func TestIssue47243_TypedRHS(t *testing.T) {
395
396
397 const src = `package issue47243; var a uint64; var _ = a << uint64(4294967296)`
398 testFiles(t, []string{"p.go"}, [][]byte{[]byte(src)}, 0, false, withSizes(&StdSizes{4, 4}))
399 }
400
401 func TestCheck(t *testing.T) {
402 old := buildcfg.Experiment.RangeFunc
403 defer func() {
404 buildcfg.Experiment.RangeFunc = old
405 }()
406 buildcfg.Experiment.RangeFunc = true
407
408 DefPredeclaredTestFuncs()
409 testDirFiles(t, "../../../../internal/types/testdata/check", 50, false)
410 }
411 func TestSpec(t *testing.T) { testDirFiles(t, "../../../../internal/types/testdata/spec", 20, false) }
412 func TestExamples(t *testing.T) {
413 testDirFiles(t, "../../../../internal/types/testdata/examples", 125, false)
414 }
415 func TestFixedbugs(t *testing.T) {
416 testDirFiles(t, "../../../../internal/types/testdata/fixedbugs", 100, false)
417 }
418 func TestLocal(t *testing.T) { testDirFiles(t, "testdata/local", 0, false) }
419
420 func testDirFiles(t *testing.T, dir string, colDelta uint, manual bool) {
421 testenv.MustHaveGoBuild(t)
422 dir = filepath.FromSlash(dir)
423
424 fis, err := os.ReadDir(dir)
425 if err != nil {
426 t.Error(err)
427 return
428 }
429
430 for _, fi := range fis {
431 path := filepath.Join(dir, fi.Name())
432
433
434 if fi.IsDir() {
435 testDir(t, path, colDelta, manual)
436 } else {
437 t.Run(filepath.Base(path), func(t *testing.T) {
438 testPkg(t, []string{path}, colDelta, manual)
439 })
440 }
441 }
442 }
443
444 func testDir(t *testing.T, dir string, colDelta uint, manual bool) {
445 fis, err := os.ReadDir(dir)
446 if err != nil {
447 t.Error(err)
448 return
449 }
450
451 var filenames []string
452 for _, fi := range fis {
453 filenames = append(filenames, filepath.Join(dir, fi.Name()))
454 }
455
456 t.Run(filepath.Base(dir), func(t *testing.T) {
457 testPkg(t, filenames, colDelta, manual)
458 })
459 }
460
461 func testPkg(t *testing.T, filenames []string, colDelta uint, manual bool) {
462 srcs := make([][]byte, len(filenames))
463 for i, filename := range filenames {
464 src, err := os.ReadFile(filename)
465 if err != nil {
466 t.Fatalf("could not read %s: %v", filename, err)
467 }
468 srcs[i] = src
469 }
470 testFiles(t, filenames, srcs, colDelta, manual)
471 }
472
View as plain text