Skip to content

Commit 4944b31

Browse files
griesemereric
authored andcommitted
go/types: use commentMap to collect error comments
Adjust the testFiles function to use the new commentMap function. This makes it possible for testFiles to match the types2.TestFiles logic more closely. For golang#51006. Change-Id: I6c5ecbeb86d095404ec04ba4452fb90d404b8280 Reviewed-on: https://go-review.googlesource.com/c/go/+/456137 Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> Auto-Submit: Robert Griesemer <[email protected]>
1 parent 0adb0a3 commit 4944b31

File tree

2 files changed

+121
-139
lines changed

2 files changed

+121
-139
lines changed

src/cmd/compile/internal/types2/check_test.go

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,27 +57,23 @@ func parseFiles(t *testing.T, filenames []string, mode syntax.Mode) ([]*syntax.F
5757
return files, errlist
5858
}
5959

60-
func unpackError(err error) syntax.Error {
60+
func unpackError(err error) (syntax.Pos, string) {
6161
switch err := err.(type) {
6262
case syntax.Error:
63-
return err
63+
return err.Pos, err.Msg
6464
case Error:
65-
return syntax.Error{Pos: err.Pos, Msg: err.Msg}
65+
return err.Pos, err.Msg
6666
default:
67-
return syntax.Error{Msg: err.Error()}
67+
return nopos, err.Error()
6868
}
6969
}
7070

7171
// delta returns the absolute difference between x and y.
7272
func delta(x, y uint) uint {
73-
switch {
74-
case x < y:
73+
if x < y {
7574
return y - x
76-
case x > y:
77-
return x - y
78-
default:
79-
return 0
8075
}
76+
return x - y
8177
}
8278

8379
// Note: parseFlags is identical to the version in go/types which is
@@ -171,8 +167,8 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
171167

172168
// sort errlist in source order
173169
sort.Slice(errlist, func(i, j int) bool {
174-
pi := unpackError(errlist[i]).Pos
175-
pj := unpackError(errlist[j]).Pos
170+
pi, _ := unpackError(errlist[i])
171+
pj, _ := unpackError(errlist[j])
176172
return pi.Cmp(pj) < 0
177173
})
178174

@@ -192,21 +188,20 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
192188

193189
// match against found errors
194190
for _, err := range errlist {
195-
got := unpackError(err)
191+
gotPos, gotMsg := unpackError(err)
196192

197193
// find list of errors for the respective error line
198-
filename := got.Pos.Base().Filename()
194+
filename := gotPos.Base().Filename()
199195
filemap := errmap[filename]
200-
line := got.Pos.Line()
201-
var list []syntax.Error
196+
line := gotPos.Line()
197+
var errList []syntax.Error
202198
if filemap != nil {
203-
list = filemap[line]
199+
errList = filemap[line]
204200
}
205-
// list may be nil
206201

207-
// one of errors in list should match the current error
208-
index := -1 // list index of matching message, if any
209-
for i, want := range list {
202+
// one of errors in errList should match the current error
203+
index := -1 // errList index of matching message, if any
204+
for i, want := range errList {
210205
pattern := strings.TrimSpace(want.Msg[len(" ERROR "):])
211206
if n := len(pattern); n >= 2 && pattern[0] == '"' && pattern[n-1] == '"' {
212207
pattern = pattern[1 : n-1]
@@ -216,29 +211,29 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
216211
t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
217212
continue
218213
}
219-
if rx.MatchString(got.Msg) {
214+
if rx.MatchString(gotMsg) {
220215
index = i
221216
break
222217
}
223218
}
224219
if index < 0 {
225-
t.Errorf("%s: no error expected: %q", got.Pos, got.Msg)
220+
t.Errorf("%s: no error expected: %q", gotPos, gotMsg)
226221
continue
227222
}
228223

229224
// column position must be within expected colDelta
230-
want := list[index]
231-
if delta(got.Pos.Col(), want.Pos.Col()) > colDelta {
232-
t.Errorf("%s: got col = %d; want %d", got.Pos, got.Pos.Col(), want.Pos.Col())
225+
want := errList[index]
226+
if delta(gotPos.Col(), want.Pos.Col()) > colDelta {
227+
t.Errorf("%s: got col = %d; want %d", gotPos, gotPos.Col(), want.Pos.Col())
233228
}
234229

235-
// eliminate from list
236-
if n := len(list) - 1; n > 0 {
230+
// eliminate from errList
231+
if n := len(errList) - 1; n > 0 {
237232
// not the last entry - slide entries down (don't reorder)
238-
copy(list[index:], list[index+1:])
239-
filemap[line] = list[:n]
233+
copy(errList[index:], errList[index+1:])
234+
filemap[line] = errList[:n]
240235
} else {
241-
// last entry - remove list from filemap
236+
// last entry - remove errList from filemap
242237
delete(filemap, line)
243238
}
244239

@@ -252,8 +247,8 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
252247
if len(errmap) > 0 {
253248
t.Errorf("--- %s: unreported errors:", pkgName)
254249
for filename, filemap := range errmap {
255-
for line, list := range filemap {
256-
for _, err := range list {
250+
for line, errList := range filemap {
251+
for _, err := range errList {
257252
t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
258253
}
259254
}

src/go/types/check_test.go

Lines changed: 93 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"path/filepath"
3737
"reflect"
3838
"regexp"
39+
"sort"
3940
"strings"
4041
"testing"
4142

@@ -49,21 +50,6 @@ var (
4950

5051
var fset = token.NewFileSet()
5152

52-
// Positioned errors are of the form filename:line:column: message .
53-
var posMsgRx = regexp.MustCompile(`^(.*:\d+:\d+): *(?s)(.*)`)
54-
55-
// splitError splits an error's error message into a position string
56-
// and the actual error message. If there's no position information,
57-
// pos is the empty string, and msg is the entire error message.
58-
func splitError(err error) (pos, msg string) {
59-
msg = err.Error()
60-
if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
61-
pos = m[1]
62-
msg = m[2]
63-
}
64-
return
65-
}
66-
6753
func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode parser.Mode) ([]*ast.File, []error) {
6854
var files []*ast.File
6955
var errlist []error
@@ -86,87 +72,22 @@ func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode parser.Mod
8672
return files, errlist
8773
}
8874

89-
// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
90-
// rx is a regular expression that matches the expected error message.
91-
// Space around "rx" or rx is ignored.
92-
var errRx = regexp.MustCompile(`^ *ERROR *"?([^"]*)"?`)
93-
94-
// errMap collects the regular expressions of ERROR comments found
95-
// in files and returns them as a map of error positions to error messages.
96-
//
97-
// srcs must be a slice of the same length as files, containing the original
98-
// source for the parsed AST.
99-
func errMap(t *testing.T, files []*ast.File, srcs [][]byte) map[string][]string {
100-
// map of position strings to lists of error message patterns
101-
errmap := make(map[string][]string)
102-
103-
for i, file := range files {
104-
tok := fset.File(file.Package)
105-
src := srcs[i]
106-
var s scanner.Scanner
107-
s.Init(tok, src, nil, scanner.ScanComments)
108-
var prev token.Pos // position of last non-comment, non-semicolon token
109-
110-
scanFile:
111-
for {
112-
pos, tok, lit := s.Scan()
113-
switch tok {
114-
case token.EOF:
115-
break scanFile
116-
case token.COMMENT:
117-
if lit[1] == '*' {
118-
lit = lit[:len(lit)-2] // strip trailing */
119-
}
120-
if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 2 {
121-
p := fset.Position(prev).String()
122-
errmap[p] = append(errmap[p], strings.TrimSpace(s[1]))
123-
}
124-
case token.SEMICOLON:
125-
// ignore automatically inserted semicolon
126-
if lit == "\n" {
127-
continue scanFile
128-
}
129-
fallthrough
130-
default:
131-
prev = pos
132-
}
133-
}
75+
func unpackError(fset *token.FileSet, err error) (token.Position, string) {
76+
switch err := err.(type) {
77+
case *scanner.Error:
78+
return err.Pos, err.Msg
79+
case Error:
80+
return fset.Position(err.Pos), err.Msg
13481
}
135-
136-
return errmap
82+
panic("unreachable")
13783
}
13884

139-
func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
140-
for _, err := range errlist {
141-
pos, gotMsg := splitError(err)
142-
list := errmap[pos]
143-
index := -1 // list index of matching message, if any
144-
// we expect one of the messages in list to match the error at pos
145-
for i, wantRx := range list {
146-
rx, err := regexp.Compile(wantRx)
147-
if err != nil {
148-
t.Errorf("%s: %v", pos, err)
149-
continue
150-
}
151-
if rx.MatchString(gotMsg) {
152-
index = i
153-
break
154-
}
155-
}
156-
if index >= 0 {
157-
// eliminate from list
158-
if n := len(list) - 1; n > 0 {
159-
// not the last entry - swap in last element and shorten list by 1
160-
list[index] = list[n]
161-
errmap[pos] = list[:n]
162-
} else {
163-
// last entry - remove list from map
164-
delete(errmap, pos)
165-
}
166-
} else {
167-
t.Errorf("%s: no error expected: %q", pos, gotMsg)
168-
}
85+
// delta returns the absolute difference between x and y.
86+
func delta(x, y int) int {
87+
if x < y {
88+
return y - x
16989
}
90+
return x - y
17091
}
17192

17293
// parseFlags parses flags from the first line of the given source
@@ -262,28 +183,94 @@ func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, man
262183
return
263184
}
264185

186+
// sort errlist in source order
187+
sort.Slice(errlist, func(i, j int) bool {
188+
// TODO(gri) This is not correct as scanner.Errors
189+
// don't have a correctly set Offset. But we only
190+
// care about sorting when multiple equal errors
191+
// appear on the same line, which happens with some
192+
// type checker errors.
193+
// For now this works. Will remove need for sorting
194+
// in a subsequent CL.
195+
pi, _ := unpackError(fset, errlist[i])
196+
pj, _ := unpackError(fset, errlist[j])
197+
return pi.Offset < pj.Offset
198+
})
199+
200+
// collect expected errors
201+
errmap := make(map[string]map[int][]comment)
202+
for i, filename := range filenames {
203+
if m := commentMap(srcs[i], regexp.MustCompile("^ ERROR ")); len(m) > 0 {
204+
errmap[filename] = m
205+
}
206+
}
207+
208+
// match against found errors
265209
for _, err := range errlist {
266-
err, ok := err.(Error)
267-
if !ok {
210+
gotPos, gotMsg := unpackError(fset, err)
211+
212+
// find list of errors for the respective error line
213+
filename := gotPos.Filename
214+
filemap := errmap[filename]
215+
line := gotPos.Line
216+
var errList []comment
217+
if filemap != nil {
218+
errList = filemap[line]
219+
}
220+
221+
// one of errors in errList should match the current error
222+
index := -1 // errList index of matching message, if any
223+
for i, want := range errList {
224+
pattern := strings.TrimSpace(want.text[len(" ERROR "):])
225+
if n := len(pattern); n >= 2 && pattern[0] == '"' && pattern[n-1] == '"' {
226+
pattern = pattern[1 : n-1]
227+
}
228+
rx, err := regexp.Compile(pattern)
229+
if err != nil {
230+
t.Errorf("%s:%d:%d: %v", filename, line, want.col, err)
231+
continue
232+
}
233+
if rx.MatchString(gotMsg) {
234+
index = i
235+
break
236+
}
237+
}
238+
if index < 0 {
239+
t.Errorf("%s: no error expected: %q", gotPos, gotMsg)
268240
continue
269241
}
270-
code := readCode(err)
271-
if code == 0 {
272-
t.Errorf("missing error code: %v", err)
242+
243+
// column position must be within expected colDelta
244+
const colDelta = 0
245+
want := errList[index]
246+
if delta(gotPos.Column, want.col) > colDelta {
247+
t.Errorf("%s: got col = %d; want %d", gotPos, gotPos.Column, want.col)
273248
}
274-
}
275249

276-
// match and eliminate errors;
277-
// we are expecting the following errors
278-
errmap := errMap(t, files, srcs)
279-
eliminate(t, errmap, errlist)
250+
// eliminate from errList
251+
if n := len(errList) - 1; n > 0 {
252+
// not the last entry - slide entries down (don't reorder)
253+
copy(errList[index:], errList[index+1:])
254+
filemap[line] = errList[:n]
255+
} else {
256+
// last entry - remove errList from filemap
257+
delete(filemap, line)
258+
}
259+
260+
// if filemap is empty, eliminate from errmap
261+
if len(filemap) == 0 {
262+
delete(errmap, filename)
263+
}
264+
}
280265

281266
// there should be no expected errors left
282267
if len(errmap) > 0 {
283-
t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
284-
for pos, list := range errmap {
285-
for _, rx := range list {
286-
t.Errorf("%s: %q", pos, rx)
268+
t.Errorf("--- %s: unreported errors:", pkgName)
269+
for filename, filemap := range errmap {
270+
for line, errList := range filemap {
271+
for _, err := range errList {
272+
t.Errorf("%s:%d:%d: %s", filename, line, err.col, err.text)
273+
}
287274
}
288275
}
289276
}

0 commit comments

Comments
 (0)