Source file
src/go/parser/error_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 package parser
24
25 import (
26 "flag"
27 "go/scanner"
28 "go/token"
29 "os"
30 "path/filepath"
31 "regexp"
32 "strings"
33 "testing"
34 )
35
36 var traceErrs = flag.Bool("trace_errs", false, "whether to enable tracing for error tests")
37
38 const testdata = "testdata"
39
40
41 func getFile(fset *token.FileSet, filename string) (file *token.File) {
42 fset.Iterate(func(f *token.File) bool {
43 if f.Name() == filename {
44 if file != nil {
45 panic(filename + " used multiple times")
46 }
47 file = f
48 }
49 return true
50 })
51 return file
52 }
53
54 func getPos(fset *token.FileSet, filename string, offset int) token.Pos {
55 if f := getFile(fset, filename); f != nil {
56 return f.Pos(offset)
57 }
58 return token.NoPos
59 }
60
61
62
63
64
65
66
67 var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE|AFTER)? *"([^"]*)" *\*/$`)
68
69
70
71 func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token.Pos]string {
72 errors := make(map[token.Pos]string)
73
74 var s scanner.Scanner
75
76
77
78 s.Init(getFile(fset, filename), src, nil, scanner.ScanComments)
79 var prev token.Pos
80 var here token.Pos
81
82 for {
83 pos, tok, lit := s.Scan()
84 end := s.End()
85
86 switch tok {
87 case token.EOF:
88 return errors
89 case token.COMMENT:
90 s := errRx.FindStringSubmatch(lit)
91 if len(s) == 3 {
92 if s[1] == "HERE" {
93 pos = here
94 } else if s[1] == "AFTER" {
95 pos += token.Pos(len(lit))
96 } else {
97 pos = prev
98 }
99 errors[pos] = s[2]
100 }
101 case token.SEMICOLON:
102
103 if lit != ";" {
104 break
105 }
106 fallthrough
107 default:
108 prev = pos
109 here = end
110 }
111 }
112 }
113
114
115
116 func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) {
117 t.Helper()
118 for _, error := range found {
119
120
121 pos := getPos(fset, error.Pos.Filename, error.Pos.Offset)
122 if msg, found := expected[pos]; found {
123
124 rx, err := regexp.Compile(msg)
125 if err != nil {
126 t.Errorf("%s: %v", error.Pos, err)
127 continue
128 }
129 if match := rx.MatchString(error.Msg); !match {
130 t.Errorf("%s: %q does not match %q", error.Pos, error.Msg, msg)
131 continue
132 }
133
134 delete(expected, pos)
135 } else {
136
137
138
139
140 t.Errorf("%s: unexpected error: %s", error.Pos, error.Msg)
141 }
142 }
143
144
145 if len(expected) > 0 {
146 t.Errorf("%d errors not reported:", len(expected))
147 for pos, msg := range expected {
148 t.Errorf("%s: %s\n", fset.Position(pos), msg)
149 }
150 }
151 }
152
153 func checkErrors(t *testing.T, filename string, input any, mode Mode, expectErrors bool) {
154 t.Helper()
155 src, err := readSource(filename, input)
156 if err != nil {
157 t.Error(err)
158 return
159 }
160
161 fset := token.NewFileSet()
162 _, err = ParseFile(fset, filename, src, mode)
163 found, ok := err.(scanner.ErrorList)
164 if err != nil && !ok {
165 t.Error(err)
166 return
167 }
168 found.RemoveMultiples()
169
170 expected := map[token.Pos]string{}
171 if expectErrors {
172
173
174 expected = expectedErrors(fset, filename, src)
175 }
176
177
178 compareErrors(t, fset, expected, found)
179 }
180
181 func TestErrors(t *testing.T) {
182 list, err := os.ReadDir(testdata)
183 if err != nil {
184 t.Fatal(err)
185 }
186 for _, d := range list {
187 name := d.Name()
188 t.Run(name, func(t *testing.T) {
189 if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
190 mode := DeclarationErrors | AllErrors
191 if *traceErrs {
192 mode |= Trace
193 }
194 checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
195 }
196 })
197 }
198 }
199
View as plain text