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