mirror of
https://github.com/golang/go.git
synced 2025-05-17 13:24:38 +00:00
encoding/json: Include the offset of a SyntaxError
When a SyntaxError occurs, report the current offset within the stream. The code already accounted for the offset within the current buffer being scanned. By including how much data was already scanned, the current offset can be computed. Fixes #22478 Change-Id: I91ecd4cad0b85a5c1556bc597f3ee914e769af01 Reviewed-on: https://go-review.googlesource.com/74251 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com> Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
6fac139830
commit
f1ce59d988
@ -16,6 +16,7 @@ type Decoder struct {
|
||||
buf []byte
|
||||
d decodeState
|
||||
scanp int // start of unread data in buf
|
||||
scanned int64 // amount of data already scanned
|
||||
scan scanner
|
||||
err error
|
||||
|
||||
@ -55,7 +56,7 @@ func (dec *Decoder) Decode(v interface{}) error {
|
||||
}
|
||||
|
||||
if !dec.tokenValueAllowed() {
|
||||
return &SyntaxError{msg: "not at beginning of value"}
|
||||
return &SyntaxError{msg: "not at beginning of value", Offset: dec.offset()}
|
||||
}
|
||||
|
||||
// Read whole value into buffer.
|
||||
@ -140,6 +141,7 @@ func (dec *Decoder) refill() error {
|
||||
// Make room to read more into the buffer.
|
||||
// First slide down data already consumed.
|
||||
if dec.scanp > 0 {
|
||||
dec.scanned += int64(dec.scanp)
|
||||
n := copy(dec.buf, dec.buf[dec.scanp:])
|
||||
dec.buf = dec.buf[:n]
|
||||
dec.scanp = 0
|
||||
@ -306,7 +308,7 @@ func (dec *Decoder) tokenPrepareForDecode() error {
|
||||
return err
|
||||
}
|
||||
if c != ',' {
|
||||
return &SyntaxError{"expected comma after array element", 0}
|
||||
return &SyntaxError{"expected comma after array element", dec.offset()}
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenArrayValue
|
||||
@ -316,7 +318,7 @@ func (dec *Decoder) tokenPrepareForDecode() error {
|
||||
return err
|
||||
}
|
||||
if c != ':' {
|
||||
return &SyntaxError{"expected colon after object key", 0}
|
||||
return &SyntaxError{"expected colon after object key", dec.offset()}
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenObjectValue
|
||||
@ -433,7 +435,6 @@ func (dec *Decoder) Token() (Token, error) {
|
||||
err := dec.Decode(&x)
|
||||
dec.tokenState = old
|
||||
if err != nil {
|
||||
clearOffset(err)
|
||||
return nil, err
|
||||
}
|
||||
dec.tokenState = tokenObjectColon
|
||||
@ -447,7 +448,6 @@ func (dec *Decoder) Token() (Token, error) {
|
||||
}
|
||||
var x interface{}
|
||||
if err := dec.Decode(&x); err != nil {
|
||||
clearOffset(err)
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
@ -455,12 +455,6 @@ func (dec *Decoder) Token() (Token, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func clearOffset(err error) {
|
||||
if s, ok := err.(*SyntaxError); ok {
|
||||
s.Offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) tokenError(c byte) (Token, error) {
|
||||
var context string
|
||||
switch dec.tokenState {
|
||||
@ -477,7 +471,7 @@ func (dec *Decoder) tokenError(c byte) (Token, error) {
|
||||
case tokenObjectComma:
|
||||
context = " after object key:value pair"
|
||||
}
|
||||
return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, 0}
|
||||
return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, dec.offset()}
|
||||
}
|
||||
|
||||
// More reports whether there is another element in the
|
||||
@ -505,3 +499,7 @@ func (dec *Decoder) peek() (byte, error) {
|
||||
err = dec.refill()
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) offset() int64 {
|
||||
return dec.scanned + int64(dec.scanp)
|
||||
}
|
||||
|
@ -342,11 +342,18 @@ var tokenStreamCases []tokenStreamCase = []tokenStreamCase{
|
||||
{json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{
|
||||
Delim('['),
|
||||
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||
decodeThis{&SyntaxError{"expected comma after array element", 0}},
|
||||
decodeThis{&SyntaxError{"expected comma after array element", 11}},
|
||||
}},
|
||||
{json: `{ "a" 1 }`, expTokens: []interface{}{
|
||||
Delim('{'), "a",
|
||||
decodeThis{&SyntaxError{"expected colon after object key", 0}},
|
||||
{json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []interface{}{
|
||||
Delim('{'), strings.Repeat("a", 513),
|
||||
decodeThis{&SyntaxError{"expected colon after object key", 518}},
|
||||
}},
|
||||
{json: `{ "\a" }`, expTokens: []interface{}{
|
||||
Delim('{'),
|
||||
&SyntaxError{"invalid character 'a' in string escape code", 3},
|
||||
}},
|
||||
{json: ` \a`, expTokens: []interface{}{
|
||||
&SyntaxError{"invalid character '\\\\' looking for beginning of value", 1},
|
||||
}},
|
||||
}
|
||||
|
||||
@ -367,15 +374,15 @@ func TestDecodeInStream(t *testing.T) {
|
||||
tk, err = dec.Token()
|
||||
}
|
||||
if experr, ok := etk.(error); ok {
|
||||
if err == nil || err.Error() != experr.Error() {
|
||||
t.Errorf("case %v: Expected error %v in %q, but was %v", ci, experr, tcase.json, err)
|
||||
if err == nil || !reflect.DeepEqual(err, experr) {
|
||||
t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err)
|
||||
}
|
||||
break
|
||||
} else if err == io.EOF {
|
||||
t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json)
|
||||
break
|
||||
} else if err != nil {
|
||||
t.Errorf("case %v: Unexpected error '%v' in %q", ci, err, tcase.json)
|
||||
t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json)
|
||||
break
|
||||
}
|
||||
if !reflect.DeepEqual(tk, etk) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user