From: Matt Joiner Date: Thu, 12 Aug 2021 03:46:02 +0000 (+1000) Subject: Don't panic on int parse failures X-Git-Tag: v1.30.2~1 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=901a8b1b366ebfb42efea01813eb9019745075c9;p=btrtrc.git Don't panic on int parse failures This means for UnmarshalTypeErrors we now include context. There are still some other error types remaining that are thrown up via panic. --- diff --git a/bencode/api.go b/bencode/api.go index 80d9951b..3c379abb 100644 --- a/bencode/api.go +++ b/bencode/api.go @@ -41,12 +41,18 @@ func (e *UnmarshalInvalidArgError) Error() string { // Unmarshaler spotted a value that was not appropriate for a given Go value. type UnmarshalTypeError struct { - Value string - Type reflect.Type + BencodeTypeName string + UnmarshalTargetType reflect.Type } +// This could probably be a value type, but we may already have users assuming +// that it's passed by pointer. func (e *UnmarshalTypeError) Error() string { - return fmt.Sprintf("cannot unmarshal a bencode %s into a %s", e.Value, e.Type) + return fmt.Sprintf( + "can't unmarshal a bencode %v into a %v", + e.BencodeTypeName, + e.UnmarshalTargetType, + ) } // Unmarshaler tried to write to an unexported (therefore unwritable) field. diff --git a/bencode/decode.go b/bencode/decode.go index f8ec8295..5dbcbe5a 100644 --- a/bencode/decode.go +++ b/bencode/decode.go @@ -102,7 +102,7 @@ func (d *Decoder) throwSyntaxError(offset int64, err error) { } // called when 'i' was consumed -func (d *Decoder) parseInt(v reflect.Value) { +func (d *Decoder) parseInt(v reflect.Value) error { start := d.Offset - 1 d.readUntil('e') if d.buf.Len() == 0 { @@ -120,10 +120,10 @@ func (d *Decoder) parseInt(v reflect.Value) { checkForIntParseError(err, start) if v.OverflowInt(n) { - panic(&UnmarshalTypeError{ - Value: "integer " + s, - Type: v.Type(), - }) + return &UnmarshalTypeError{ + BencodeTypeName: "int", + UnmarshalTargetType: v.Type(), + } } v.SetInt(n) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: @@ -131,21 +131,22 @@ func (d *Decoder) parseInt(v reflect.Value) { checkForIntParseError(err, start) if v.OverflowUint(n) { - panic(&UnmarshalTypeError{ - Value: "integer " + s, - Type: v.Type(), - }) + return &UnmarshalTypeError{ + BencodeTypeName: "int", + UnmarshalTargetType: v.Type(), + } } v.SetUint(n) case reflect.Bool: v.SetBool(s != "0") default: - panic(&UnmarshalTypeError{ - Value: "integer " + s, - Type: v.Type(), - }) + return &UnmarshalTypeError{ + BencodeTypeName: "int", + UnmarshalTargetType: v.Type(), + } } d.buf.Reset() + return nil } func (d *Decoder) parseString(v reflect.Value) error { @@ -198,8 +199,8 @@ func (d *Decoder) parseString(v reflect.Value) error { read(d.buf.Bytes()[:length]) // I believe we return here to support "ignore_unmarshal_type_error". return &UnmarshalTypeError{ - Value: "string", - Type: v.Type(), + BencodeTypeName: "string", + UnmarshalTargetType: v.Type(), } } @@ -238,7 +239,7 @@ func getDictField(dict reflect.Type, key string) (_ dictField, err error) { // }) //} default: - err = fmt.Errorf("can't parse bencode dict items into %v", k) + err = fmt.Errorf("can't assign bencode dict items into a %v", k) return } } @@ -347,8 +348,9 @@ func (d *Decoder) parseDict(v reflect.Value) error { //log.Printf("parsing into %v", setValue.Type()) ok, err = d.parseValue(setValue) if err != nil { - if _, ok := err.(*UnmarshalTypeError); !ok || !df.Tags.IgnoreUnmarshalTypeError() { - return fmt.Errorf("parsing value for key %q: %s", keyStr, err) + var target *UnmarshalTypeError + if !(errors.As(err, &target) && df.Tags.IgnoreUnmarshalTypeError()) { + return fmt.Errorf("parsing value for key %q: %w", keyStr, err) } } if !ok { @@ -369,8 +371,8 @@ func (d *Decoder) parseList(v reflect.Value) error { } if l.Elem().Len() != 1 { return &UnmarshalTypeError{ - Value: "list", - Type: v.Type(), + BencodeTypeName: "list", + UnmarshalTargetType: v.Type(), } } v.Set(l.Elem().Index(0)) @@ -527,8 +529,7 @@ func (d *Decoder) parseValue(v reflect.Value) (bool, error) { case 'l': return true, d.parseList(v) case 'i': - d.parseInt(v) - return true, nil + return true, d.parseInt(v) default: if b >= '0' && b <= '9' { // It's a string. diff --git a/bencode/decode_test.go b/bencode/decode_test.go index dc610daf..e9de91bd 100644 --- a/bencode/decode_test.go +++ b/bencode/decode_test.go @@ -183,3 +183,12 @@ func TestDecodeDictIntoUnsupported(t *testing.T) { t.Log(err) c.Check(err, qt.Not(qt.IsNil)) } + +func TestUnmarshalDictKeyNotString(t *testing.T) { + // Any type that a dict shouldn't be unmarshallable into. + var i int + c := qt.New(t) + err := Unmarshal([]byte("di42e3:yese"), &i) + t.Log(err) + c.Check(err, qt.Not(qt.IsNil)) +}