Skip to content

Commit

Permalink
fix: support accurate position for decoder errors
Browse files Browse the repository at this point in the history
  • Loading branch information
liuq19 committed Apr 25, 2024
1 parent db8f69d commit 82361b9
Show file tree
Hide file tree
Showing 21 changed files with 788 additions and 739 deletions.
6 changes: 6 additions & 0 deletions api_test.go
Expand Up @@ -63,6 +63,11 @@ func TestApiUseNumber(t *testing.T) {
err := cfg.UnmarshalFromString("123", &i)
assert.NoError(t, err)
assert.Equal(t, i, interface{}(json.Number("123")))

var i2 int
err = cfg.UnmarshalFromString("123", &i2)
assert.NoError(t, err)
assert.Equal(t, i2, 123)
}

func TestApiUseInt64(t *testing.T) {
Expand All @@ -81,6 +86,7 @@ func TestApiDefaultNoCopy(t *testing.T) {
// not copy the string
cfg := Config{
}.Froze()

var i string
data := "\"123\""
ptr0 := (*reflect.StringHeader)(unsafe.Pointer(&data)).Data + 1
Expand Down
8 changes: 4 additions & 4 deletions decode_test.go
Expand Up @@ -2028,10 +2028,10 @@ var decodeMismatchErrorTests = []struct {
dest interface{}
src string
}{
{new(int), `{}`},
{new(string), `{}`},
{new(bool), `{}`},
{new([]byte), `{}`},
{new(int), `{}`},
{new(string), `{}`},
{new(bool), `{}`},
{new([]byte), `{}`},
}

func TestMismatchTypeError(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions dev/decoder/compile_struct.go
Expand Up @@ -141,5 +141,6 @@ func (c *compiler) compileStructBody(vt reflect.Type) decFunc {
fieldMap: fm,
fields: entries,
structName: vt.Name(),
typ: vt,
}
}
6 changes: 6 additions & 0 deletions dev/decoder/compiler.go
Expand Up @@ -197,6 +197,7 @@ func (c *compiler) compileArray(vt reflect.Type) decFunc {
len: vt.Len(),
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}

Expand All @@ -215,13 +216,15 @@ func (c *compiler) tryCompileSliceUnmarshaler(vt reflect.Type) decFunc {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}

if pt.Implements(encodingTextUnmarshalerType) {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
return nil
Expand Down Expand Up @@ -266,6 +269,7 @@ func (c *compiler) compileSlice(vt reflect.Type) decFunc {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}

Expand All @@ -276,13 +280,15 @@ func (c *compiler) compileSliceBytes(vt reflect.Type) decFunc {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}

if ep.Implements(encodingTextUnmarshalerType) {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}

Expand Down
44 changes: 18 additions & 26 deletions dev/decoder/errors.go
Expand Up @@ -118,9 +118,11 @@ func error_type(vt *rt.GoType) error {
}

type MismatchTypeError struct {
Pos int
Src string
Type reflect.Type
Pos int
Src string
Type reflect.Type
Struct string
Field string
}

func swithchJSONType(src string, pos int) string {
Expand All @@ -143,26 +145,27 @@ func swithchJSONType(src string, pos int) string {
}

func (self MismatchTypeError) Error() string {
se := SyntaxError{
Pos: self.Pos,
Src: self.Src,
}
return fmt.Sprintf("Mismatch type %s with value %s %q", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
return self.Description()
}

func (self MismatchTypeError) Description() string {
se := SyntaxError{
Pos: self.Pos,
Src: self.Src,
}
return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
if self.Struct != "" {
return fmt.Sprintf("Mismatch type `%s` in struct `%s` field `%s` with value `%s` %s", self.Type.String(), self.Struct, self.Field, swithchJSONType(self.Src, self.Pos), se.description())
} else {
return fmt.Sprintf("Mismatch type `%s` with value `%s` %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
}

}

func error_mismatch(src string, pos int, vt *rt.GoType) error {
func error_mismatch(node internal.Node, ctx *context, typ reflect.Type) error {
return &MismatchTypeError{
Pos: pos,
Src: src,
Type: vt.Pack(),
Pos: node.Position(&ctx.Context),
Src: ctx.Json,
Type: typ,
}
}

Expand All @@ -187,23 +190,12 @@ func error_parse_internal(err error, src string) error {
}

if e, ok := err.(*internal.UnmatchedError); ok {
return SyntaxError{
return &MismatchTypeError {
Pos: int(e.Offset),
Src: src,
Msg: e.Msg,
Type: e.Type.Pack(),
}
}

return err
}

func error_mismatch_internal(err error, vt reflect.Type, src string) error {
if _, ok := err.(*internal.UnmatchedError); ok {
return MismatchTypeError{
Pos: 0,
Src: src,
Type: vt,
}
}
return err
}
121 changes: 115 additions & 6 deletions dev/decoder/errors_test.go
Expand Up @@ -18,20 +18,129 @@ package decoder

import (
"testing"
"encoding/json"

"github.com/stretchr/testify/assert"
)

type errTest struct {
in string
ptr interface{}
pos int
}


func TestErrors_ParseError(t *testing.T) {
var got interface{}
err := NewDecoder(`{123}`).Decode(&got)
println(err.(SyntaxError).Description())
testCases := []errTest {
{
in: `{123}`,
pos: 1,
},
{
in: `{}1`,
pos: 2,
},
{
in: `tru`,
pos: 3,
},
{
in: ` fx`,
pos: 3,
},
{
in: `{"12" 12}`,
pos: 6,
},
}

for _, tt := range testCases {
var v1, v2 interface{}
got := NewDecoder(tt.in).Decode(&v1)
exp := json.Unmarshal([]byte(tt.in), &v2)
assert.Error(t, exp)
e := got.(SyntaxError);
assert.Equal(t, tt.pos, e.Pos)
println(e.Description())

}
}

type A struct {
A string
}


type B struct {
A int `json:"a,string"`
}

func TestErrors_MismatchType(t *testing.T) {
var got A
err := NewDecoder(`{"a": 123}`).Decode(&got)
println(err.(MismatchTypeError).Description())
testCases := []errTest {
{
in: `{"a": 123}`,
ptr: &A{},
pos: 6,
},
{
in: ` {"a": true}`,
ptr: &A{},
pos: 7,
},
{
in: ` {"a": true}`,
ptr: &B{},
pos: 7,
},
{
in: ` {"a": "true"}`,
ptr: &B{},
pos: 7,
},
{
in: ` [1, 2, "3", 4]`,
ptr: &[4]int{},
pos: 8,
},
{
in: ` [1, 2, "3", 4]`,
ptr: &[]int{},
pos: 8,
},
{
in: ` [1, 256, "3", 4]`,
ptr: &[]int8{},
pos: 5,
},
{
in: ` [1, 256, "3", 4]`,
ptr: &[]byte{}, // []byte is special
pos: 1,
},
{
in: ` {"key": 123}`,
ptr: &map[string]string{},
pos: 9,
},
{
in: ` {"key": 123}`,
ptr: &map[int64]interface{}{},
pos: 2,
},
{
in: ` "key"`,
ptr: new(json.Number),
pos: 1,
},
}

for _, tt := range testCases {
got := NewDecoder(tt.in).Decode(tt.ptr)
e := got.(*MismatchTypeError);
assert.Equal(t, tt.pos, e.Pos)
println(e.Description())

exp := json.Unmarshal([]byte(tt.in), tt.ptr)
assert.Error(t, exp)
}
}

0 comments on commit 82361b9

Please sign in to comment.