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 switch tok {
85 case token.EOF:
86 return errors
87 case token.COMMENT:
88 s := errRx.FindStringSubmatch(lit)
89 if len(s) == 3 {
90 if s[1] == "HERE" {
91 pos = here
92 } else if s[1] == "AFTER" {
93 pos += token.Pos(len(lit))
94 } else {
95 pos = prev
96 }
97 errors[pos] = s[2]
98 }
99 case token.SEMICOLON:
100
101 if lit != ";" {
102 break
103 }
104 fallthrough
105 default:
106 prev = pos
107 var l int
108 if tok.IsLiteral() {
109 l = len(lit)
110 } else {
111 l = len(tok.String())
112 }
113 here = prev + token.Pos(l)
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 *traceErrs {
196 mode |= Trace
197 }
198 checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
199 }
200 })
201 }
202 }
203
View as plain text