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