1
0
mirror of https://github.com/golang/go synced 2024-11-27 05:11:22 -07:00

text/scanner: don't crash when calling TokenText in error handler

Make sure Scanner.tokEnd is set before we call Scanner.Error
and update documentation accordingly.
(Until now tokEnd was only set before returning from Scan,
so a call to TokenText during error handling may have crashed.)

While at it, tighten a check in Scanner.TokenText to ensure
Scanner.tokEnd >= Scanner.tokPos if we have a token.

Also, silence error messages to Stderr in unrelated TestIllegalExponent.

Fixes #29723.

Change-Id: Ia97beeae91eaf9e0ed3dada0a806f1f7122461cc
Reviewed-on: https://go-review.googlesource.com/c/157819
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Robert Griesemer 2019-01-14 13:34:42 -08:00
parent 69de40c9af
commit 85c1798ac6
2 changed files with 23 additions and 2 deletions

View File

@ -322,6 +322,7 @@ func (s *Scanner) Peek() rune {
} }
func (s *Scanner) error(msg string) { func (s *Scanner) error(msg string) {
s.tokEnd = s.srcPos - s.lastCharLen // make sure token text is terminated
s.ErrorCount++ s.ErrorCount++
if s.Error != nil { if s.Error != nil {
s.Error(s, msg) s.Error(s, msg)
@ -664,17 +665,18 @@ func (s *Scanner) Pos() (pos Position) {
} }
// TokenText returns the string corresponding to the most recently scanned token. // TokenText returns the string corresponding to the most recently scanned token.
// Valid after calling Scan(). // Valid after calling Scan and in calls of Scanner.Error.
func (s *Scanner) TokenText() string { func (s *Scanner) TokenText() string {
if s.tokPos < 0 { if s.tokPos < 0 {
// no token text // no token text
return "" return ""
} }
if s.tokEnd < 0 { if s.tokEnd < s.tokPos {
// if EOF was reached, s.tokEnd is set to -1 (s.srcPos == 0) // if EOF was reached, s.tokEnd is set to -1 (s.srcPos == 0)
s.tokEnd = s.tokPos s.tokEnd = s.tokPos
} }
// s.tokEnd >= s.tokPos
if s.tokBuf.Len() == 0 { if s.tokBuf.Len() == 0 {
// common case: the entire token text is still in srcBuf // common case: the entire token text is still in srcBuf

View File

@ -293,6 +293,12 @@ func TestScan(t *testing.T) {
func TestIllegalExponent(t *testing.T) { func TestIllegalExponent(t *testing.T) {
const src = "1.5e 1.5E 1e+ 1e- 1.5z" const src = "1.5e 1.5E 1e+ 1e- 1.5z"
s := new(Scanner).Init(strings.NewReader(src)) s := new(Scanner).Init(strings.NewReader(src))
s.Error = func(s *Scanner, msg string) {
const want = "illegal exponent"
if msg != want {
t.Errorf("%s: got error %q; want %q", s.TokenText(), msg, want)
}
}
checkTokErr(t, s, 1, Float, "1.5e") checkTokErr(t, s, 1, Float, "1.5e")
checkTokErr(t, s, 1, Float, "1.5E") checkTokErr(t, s, 1, Float, "1.5E")
checkTokErr(t, s, 1, Float, "1e+") checkTokErr(t, s, 1, Float, "1e+")
@ -692,3 +698,16 @@ func TestScanEOFHandling(t *testing.T) {
t.Errorf("scanner called Read %d times, not once", r) t.Errorf("scanner called Read %d times, not once", r)
} }
} }
func TestIssue29723(t *testing.T) {
s := new(Scanner).Init(strings.NewReader(`x "`))
s.Error = func(s *Scanner, _ string) {
got := s.TokenText() // this call shouldn't panic
const want = `"`
if got != want {
t.Errorf("got %q; want %q", got, want)
}
}
for r := s.Scan(); r != EOF; r = s.Scan() {
}
}