From 8bfb9ef0831c858fe0fd48ad2a8f98fc1ba416fc Mon Sep 17 00:00:00 2001 From: Alvar Penning Date: Thu, 1 Aug 2024 10:59:53 +0200 Subject: [PATCH] Refactor overflowError to be pretty-printable When trying to yaml.Unmarshal a negative number into an uint, the resulting error indicates an overflow with type information, but without the position. For example: > cannot unmarshal -23 into Go value of type uint64 ( overflow ) From an end user's perspective reading an error for an invalid YAML, this is quite unintuitive. Especially compared to the error message when trying to unmarshal a string into an uint. > [1:4] cannot unmarshal string into Go struct field Foo.A of type uint64 > > 1 | a: 'foo' > ^ This change has moved overflowError to internal.errors.overflowError, implementing both the PrettyPrinter and Formatter. Its implementation is uniform with those of the syntaxError and TypeError. Thus, the error from above now looks like: > [1:4] cannot unmarshal -23 into Go value of type uint64 ( overflow ) > > 1 | a: -23 > ^ --- decode.go | 17 ++--------------- internal/errors/error.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/decode.go b/decode.go index 72af5e2..5e284f3 100644 --- a/decode.go +++ b/decode.go @@ -524,19 +524,6 @@ func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) return v.Convert(typ), nil } -type overflowError struct { - dstType reflect.Type - srcNum string -} - -func (e *overflowError) Error() string { - return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) -} - -func errOverflow(dstType reflect.Type, num string) *overflowError { - return &overflowError{dstType: dstType, srcNum: num} -} - func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError { return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token} } @@ -904,7 +891,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No default: return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } - return errOverflow(valueType, fmt.Sprint(v)) + return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: v := d.nodeToValue(src) switch vv := v.(type) { @@ -936,7 +923,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No default: return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } - return errOverflow(valueType, fmt.Sprint(v)) + return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken()) } v := reflect.ValueOf(d.nodeToValue(src)) if v.IsValid() { diff --git a/internal/errors/error.go b/internal/errors/error.go index 7f1ea9a..4d3a35d 100644 --- a/internal/errors/error.go +++ b/internal/errors/error.go @@ -39,6 +39,11 @@ func ErrSyntax(msg string, tk *token.Token) *syntaxError { } } +// ErrOverflow creates an overflow error instance with message and a token. +func ErrOverflow(dstType reflect.Type, num string, tk *token.Token) *overflowError { + return &overflowError{dstType: dstType, srcNum: num, token: tk} +} + type baseError struct { state fmt.State verb rune @@ -164,6 +169,39 @@ func (e *wrapError) Error() string { return buf.String() } +type overflowError struct { + dstType reflect.Type + srcNum string + token *token.Token +} + +func (e *overflowError) Error() string { + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) +} + +func (e *overflowError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *overflowError) FormatError(p xerrors.Printer) error { + var pp printer.Printer + + var colored, inclSource bool + if fep, ok := p.(*FormatErrorPrinter); ok { + colored = fep.Colored + inclSource = fep.InclSource + } + + pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored) + if inclSource { + msg += "\n" + pp.PrintErrorToken(e.token, colored) + } + p.Print(msg) + + return nil +} + type syntaxError struct { *baseError msg string