Merge pull request #73 from WatchBeam/assert-consumed

Assert that no extraneous data remains after top-level tokens
This commit is contained in:
Victor Starodub
2016-11-02 14:22:04 +04:00
committed by GitHub
3 changed files with 66 additions and 2 deletions
+11
View File
@@ -340,10 +340,14 @@ func (g *Generator) genSliceArrayDecoder(t reflect.Type) error {
typ := g.getType(t)
fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {")
fmt.Fprintln(g.out, " isTopLevel := in.IsStart()")
err := g.genTypeDecoderNoCheck(t, "*out", fieldTags{}, 1)
if err != nil {
return err
}
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, "}")
return nil
@@ -358,7 +362,11 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
typ := g.getType(t)
fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {")
fmt.Fprintln(g.out, " isTopLevel := in.IsStart()")
fmt.Fprintln(g.out, " if in.IsNull() {")
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.Skip()")
fmt.Fprintln(g.out, " return")
fmt.Fprintln(g.out, " }")
@@ -404,6 +412,9 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.Delim('}')")
fmt.Fprintln(g.out, " if isTopLevel {")
fmt.Fprintln(g.out, " in.Consumed()")
fmt.Fprintln(g.out, " }")
for _, f := range fs {
g.genRequiredFieldCheck(t, f)
+31 -2
View File
@@ -519,8 +519,9 @@ func (r *Lexer) SkipRecursive() {
r.err = &LexerError{
Reason: "EOF reached while skipping array/object or token",
Offset: r.pos,
Data: string(r.Data[r.pos:]),
}}
Data: string(r.Data[r.pos:]),
}
}
// Raw fetches the next item recursively as a data slice
func (r *Lexer) Raw() []byte {
@@ -531,6 +532,34 @@ func (r *Lexer) Raw() []byte {
return r.Data[r.start:r.pos]
}
// IsStart returns whether the lexer is positioned at the start
// of an input string.
func (r *Lexer) IsStart() bool {
return r.pos == 0
}
// Consumed reads all remaining bytes from the input, publishing an error if
// there is anything but whitespace remaining.
func (r *Lexer) Consumed() {
if r.pos > len(r.Data) {
return
}
for _, c := range r.Data[r.pos:] {
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
r.err = &LexerError{
Reason: "invalid character '" + string(c) + "' after top-level value",
Offset: r.pos,
Data: string(r.Data[r.pos:]),
}
return
}
r.pos++
r.start++
}
}
// UnsafeString returns the string value if the token is a string literal.
//
// Warning: returned string may point to the input buffer, so the string should not outlive
+24
View File
@@ -222,3 +222,27 @@ func TestInterface(t *testing.T) {
}
}
}
func TestConsumed(t *testing.T) {
for i, test := range []struct {
toParse string
wantError bool
}{
{toParse: "", wantError: false},
{toParse: " ", wantError: false},
{toParse: "\r\n", wantError: false},
{toParse: "\t\t", wantError: false},
{toParse: "{", wantError: true},
} {
l := Lexer{Data: []byte(test.toParse)}
l.Consumed()
err := l.Error()
if err != nil && !test.wantError {
t.Errorf("[%d, %q] Consumed() error: %v", i, test.toParse, err)
} else if err == nil && test.wantError {
t.Errorf("[%d, %q] Consumed() ok; want error", i, test.toParse)
}
}
}