Skip to content

Commit 60a9bf9

Browse files
committed
cmd/compile/internal/syntax: fix error handling for Read/Parse calls
- define syntax.Error for cleaner error reporting - abort parsing after first error if no error handler is installed - make sure to always report the first error, if any - document behavior of API calls - while at it: rename ReadXXX -> ParseXXX (clearer) - adjust cmd/compile noder.go accordingly Fixes #17774. Change-Id: I7893eedea454a64acd753e32f7a8bf811ddbb03c Reviewed-on: https://go-review.googlesource.com/32950 Reviewed-by: Matthew Dempsky <[email protected]>
1 parent ad02047 commit 60a9bf9

File tree

8 files changed

+126
-85
lines changed

8 files changed

+126
-85
lines changed

src/cmd/compile/internal/gc/noder.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package gc
66

77
import (
88
"fmt"
9+
"os"
910
"strconv"
1011
"strings"
1112
"unicode/utf8"
@@ -14,18 +15,21 @@ import (
1415
)
1516

1617
func parseFile(filename string) {
17-
p := noder{baseline: lexlineno}
18-
file, err := syntax.ReadFile(filename, p.error, p.pragma, 0)
18+
src, err := os.Open(filename)
1919
if err != nil {
20-
fmt.Printf("parse %s: %v\n", filename, err)
20+
fmt.Println(err)
2121
errorexit()
2222
}
23+
defer src.Close()
24+
25+
p := noder{baseline: lexlineno}
26+
file, _ := syntax.Parse(src, p.error, p.pragma, 0) // errors are tracked via p.error
2327

2428
p.file(file)
2529

2630
if !imported_unsafe {
2731
for _, x := range p.linknames {
28-
p.error(0, x, "//go:linkname only allowed in Go files that import \"unsafe\"")
32+
p.error(syntax.Error{0, x, "//go:linkname only allowed in Go files that import \"unsafe\""})
2933
}
3034
}
3135

@@ -1003,8 +1007,16 @@ func (p *noder) lineno(n syntax.Node) {
10031007
lineno = p.baseline + l - 1
10041008
}
10051009

1006-
func (p *noder) error(_, line int, msg string) {
1007-
yyerrorl(p.baseline+int32(line)-1, "%s", msg)
1010+
func (p *noder) error(err error) {
1011+
line := p.baseline
1012+
var msg string
1013+
if err, ok := err.(syntax.Error); ok {
1014+
line += int32(err.Line) - 1
1015+
msg = err.Msg
1016+
} else {
1017+
msg = err.Error()
1018+
}
1019+
yyerrorl(line, "%s", msg)
10081020
}
10091021

10101022
func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
@@ -1020,7 +1032,7 @@ func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
10201032
break
10211033
}
10221034
if n > 1e8 {
1023-
p.error(pos, line, "line number out of range")
1035+
p.error(syntax.Error{pos, line, "line number out of range"})
10241036
errorexit()
10251037
}
10261038
if n <= 0 {
@@ -1036,7 +1048,7 @@ func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
10361048

10371049
f := strings.Fields(text)
10381050
if len(f) != 3 {
1039-
p.error(pos, line, "usage: //go:linkname localname linkname")
1051+
p.error(syntax.Error{pos, line, "usage: //go:linkname localname linkname"})
10401052
break
10411053
}
10421054
lookup(f[1]).Linkname = f[2]

src/cmd/compile/internal/syntax/dumper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestDump(t *testing.T) {
1414
t.Skip("skipping test in short mode")
1515
}
1616

17-
ast, err := ReadFile(*src, nil, nil, 0)
17+
ast, err := ParseFile(*src, nil, nil, 0)
1818
if err != nil {
1919
t.Fatal(err)
2020
}

src/cmd/compile/internal/syntax/parser.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,16 @@ type parser struct {
2424
fnest int // function nesting level (for error handling)
2525
xnest int // expression nesting level (for complit ambiguity resolution)
2626
indent []byte // tracing support
27-
28-
nerrors int // error count
2927
}
3028

3129
type parserError string // for error recovery if no error handler was installed
3230

3331
func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
34-
p.scanner.init(src, func(pos, line int, msg string) {
35-
p.nerrors++
36-
if !debug && errh != nil {
37-
errh(pos, line, msg)
38-
return
39-
}
40-
panic(parserError(fmt.Sprintf("%d: %s\n", line, msg)))
41-
}, pragh)
32+
p.scanner.init(src, errh, pragh)
4233

4334
p.fnest = 0
4435
p.xnest = 0
4536
p.indent = nil
46-
47-
p.nerrors = 0
4837
}
4938

5039
func (p *parser) got(tok token) bool {
@@ -76,7 +65,7 @@ func (p *parser) syntax_error_at(pos, line int, msg string) {
7665
defer p.trace("syntax_error (" + msg + ")")()
7766
}
7867

79-
if p.tok == _EOF && p.nerrors > 0 {
68+
if p.tok == _EOF && p.first != nil {
8069
return // avoid meaningless follow-up errors
8170
}
8271

@@ -207,7 +196,7 @@ func (p *parser) file() *File {
207196
p.want(_Semi)
208197

209198
// don't bother continuing if package clause has errors
210-
if p.nerrors > 0 {
199+
if p.first != nil {
211200
return nil
212201
}
213202

src/cmd/compile/internal/syntax/parser_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var src = flag.String("src", "parser.go", "source file to parse")
2222
var verify = flag.Bool("verify", false, "verify idempotent printing")
2323

2424
func TestParse(t *testing.T) {
25-
_, err := ReadFile(*src, nil, nil, 0)
25+
_, err := ParseFile(*src, nil, nil, 0)
2626
if err != nil {
2727
t.Fatal(err)
2828
}
@@ -52,7 +52,7 @@ func TestStdLib(t *testing.T) {
5252
if debug {
5353
fmt.Printf("parsing %s\n", filename)
5454
}
55-
ast, err := ReadFile(filename, nil, nil, 0)
55+
ast, err := ParseFile(filename, nil, nil, 0)
5656
if err != nil {
5757
t.Error(err)
5858
return
@@ -133,7 +133,7 @@ func verifyPrint(filename string, ast1 *File) {
133133
panic(err)
134134
}
135135

136-
ast2, err := ReadBytes(buf1.Bytes(), nil, nil, 0)
136+
ast2, err := ParseBytes(buf1.Bytes(), nil, nil, 0)
137137
if err != nil {
138138
panic(err)
139139
}
@@ -157,8 +157,28 @@ func verifyPrint(filename string, ast1 *File) {
157157
}
158158

159159
func TestIssue17697(t *testing.T) {
160-
_, err := ReadBytes(nil, nil, nil, 0) // return with parser error, don't panic
160+
_, err := ParseBytes(nil, nil, nil, 0) // return with parser error, don't panic
161161
if err == nil {
162162
t.Errorf("no error reported")
163163
}
164164
}
165+
166+
func TestParseFile(t *testing.T) {
167+
_, err := ParseFile("", nil, nil, 0)
168+
if err == nil {
169+
t.Error("missing io error")
170+
}
171+
172+
var first error
173+
_, err = ParseFile("", func(err error) {
174+
if first == nil {
175+
first = err
176+
}
177+
}, nil, 0)
178+
if err == nil || first == nil {
179+
t.Error("missing io error")
180+
}
181+
if err != first {
182+
t.Error("got %v; want first error %v", err, first)
183+
}
184+
}

src/cmd/compile/internal/syntax/printer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestPrint(t *testing.T) {
1515
t.Skip("skipping test in short mode")
1616
}
1717

18-
ast, err := ReadFile(*src, nil, nil, 0)
18+
ast, err := ParseFile(*src, nil, nil, 0)
1919
if err != nil {
2020
t.Fatal(err)
2121
}

src/cmd/compile/internal/syntax/scanner_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -322,21 +322,22 @@ func TestScanErrors(t *testing.T) {
322322
} {
323323
var s scanner
324324
nerrors := 0
325-
s.init(&bytesReader{[]byte(test.src)}, func(pos, line int, msg string) {
325+
s.init(&bytesReader{[]byte(test.src)}, func(err error) {
326326
nerrors++
327327
// only check the first error
328+
e := err.(Error) // we know it's an Error
328329
if nerrors == 1 {
329-
if msg != test.msg {
330-
t.Errorf("%q: got msg = %q; want %q", test.src, msg, test.msg)
330+
if e.Msg != test.msg {
331+
t.Errorf("%q: got msg = %q; want %q", test.src, e.Msg, test.msg)
331332
}
332-
if pos != test.pos {
333-
t.Errorf("%q: got pos = %d; want %d", test.src, pos, test.pos)
333+
if e.Pos != test.pos {
334+
t.Errorf("%q: got pos = %d; want %d", test.src, e.Pos, test.pos)
334335
}
335-
if line != test.line {
336-
t.Errorf("%q: got line = %d; want %d", test.src, line, test.line)
336+
if e.Line != test.line {
337+
t.Errorf("%q: got line = %d; want %d", test.src, e.Line, test.line)
337338
}
338339
} else if nerrors > 1 {
339-
t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, msg, pos, line)
340+
t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, e.Msg, e.Pos, e.Line)
340341
}
341342
}, nil)
342343

src/cmd/compile/internal/syntax/source.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package syntax
66

77
import (
8-
"fmt"
98
"io"
109
"unicode/utf8"
1110
)
@@ -16,8 +15,9 @@ import (
1615
// suf r0 r w
1716

1817
type source struct {
19-
src io.Reader
20-
errh ErrorHandler
18+
src io.Reader
19+
errh ErrorHandler
20+
first error // first error encountered
2121

2222
// source buffer
2323
buf [4 << 10]byte
@@ -34,6 +34,7 @@ type source struct {
3434
func (s *source) init(src io.Reader, errh ErrorHandler) {
3535
s.src = src
3636
s.errh = errh
37+
s.first = nil
3738

3839
s.buf[0] = utf8.RuneSelf // terminate with sentinel
3940
s.offs = 0
@@ -50,11 +51,14 @@ func (s *source) error(msg string) {
5051
}
5152

5253
func (s *source) error_at(pos, line int, msg string) {
53-
if s.errh != nil {
54-
s.errh(pos, line, msg)
55-
return
54+
err := Error{pos, line, msg}
55+
if s.first == nil {
56+
s.first = err
5657
}
57-
panic(fmt.Sprintf("%d: %s", line, msg))
58+
if s.errh == nil {
59+
panic(s.first)
60+
}
61+
s.errh(err)
5862
}
5963

6064
// pos0 returns the byte position of the last character read.

0 commit comments

Comments
 (0)