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