Skip to content

Commit 49065cb

Browse files
committed
asm: handle EOF better
Add some error catches to prevent looping at EOF. Also give better diagnostics. Also add tests for these cases. Fixes #12656. Change-Id: I1355fc149b71c868e740bfa53de29c25d160777d Reviewed-on: https://go-review.googlesource.com/14710 Reviewed-by: Andrew Gerrand <[email protected]>
1 parent 49580db commit 49065cb

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/cmd/asm/internal/lex/input.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ func predefine(defines flags.MultiFlag) map[string]*Macro {
6363
return macros
6464
}
6565

66+
var panicOnError bool // For testing.
67+
6668
func (in *Input) Error(args ...interface{}) {
69+
if panicOnError {
70+
panic(fmt.Errorf("%s:%d: %s", in.File(), in.Line(), fmt.Sprintln(args...)))
71+
}
6772
fmt.Fprintf(os.Stderr, "%s:%d: %s", in.File(), in.Line(), fmt.Sprintln(args...))
6873
os.Exit(1)
6974
}
@@ -113,6 +118,10 @@ func (in *Input) Next() ScanToken {
113118
}
114119
fallthrough
115120
default:
121+
if tok == scanner.EOF && len(in.ifdefStack) > 0 {
122+
// We're skipping text but have run out of input with no #endif.
123+
in.Error("unclosed #ifdef or #ifndef")
124+
}
116125
in.beginningOfLine = tok == '\n'
117126
if in.enabled() {
118127
in.text = in.Stack.Text()
@@ -251,6 +260,9 @@ func (in *Input) macroDefinition(name string) ([]string, []Token) {
251260
var tokens []Token
252261
// Scan to newline. Backslashes escape newlines.
253262
for tok != '\n' {
263+
if tok == scanner.EOF {
264+
in.Error("missing newline in macro definition for %q\n", name)
265+
}
254266
if tok == '\\' {
255267
tok = in.Stack.Next()
256268
if tok != '\n' && tok != '\\' {

src/cmd/asm/internal/lex/lex_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,76 @@ func drain(input *Input) string {
258258
buf.WriteString(input.Text())
259259
}
260260
}
261+
262+
type badLexTest struct {
263+
input string
264+
error string
265+
}
266+
267+
var badLexTests = []badLexTest{
268+
{
269+
"3 #define foo bar\n",
270+
"'#' must be first item on line",
271+
},
272+
{
273+
"#ifdef foo\nhello",
274+
"unclosed #ifdef or #ifndef",
275+
},
276+
{
277+
"#ifndef foo\nhello",
278+
"unclosed #ifdef or #ifndef",
279+
},
280+
{
281+
"#ifdef foo\nhello\n#else\nbye",
282+
"unclosed #ifdef or #ifndef",
283+
},
284+
{
285+
"#define A() A()\nA()",
286+
"recursive macro invocation",
287+
},
288+
{
289+
"#define A a\n#define A a\n",
290+
"redefinition of macro",
291+
},
292+
{
293+
"#define A a",
294+
"no newline after macro definition",
295+
},
296+
}
297+
298+
func TestBadLex(t *testing.T) {
299+
for _, test := range badLexTests {
300+
input := NewInput(test.error)
301+
input.Push(NewTokenizer(test.error, strings.NewReader(test.input), nil))
302+
err := firstError(input)
303+
if err == nil {
304+
t.Errorf("%s: got no error", test.error)
305+
continue
306+
}
307+
if !strings.Contains(err.Error(), test.error) {
308+
t.Errorf("got error %q expected %q", err.Error(), test.error)
309+
}
310+
}
311+
}
312+
313+
// firstError returns the first error value triggered by the input.
314+
func firstError(input *Input) (err error) {
315+
panicOnError = true
316+
defer func() {
317+
panicOnError = false
318+
switch e := recover(); e := e.(type) {
319+
case nil:
320+
case error:
321+
err = e
322+
default:
323+
panic(e)
324+
}
325+
}()
326+
327+
for {
328+
tok := input.Next()
329+
if tok == scanner.EOF {
330+
return
331+
}
332+
}
333+
}

0 commit comments

Comments
 (0)