Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+60
-22
@@ -10,6 +10,7 @@ import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -28,8 +29,8 @@ import (
|
||||
// The name of an unknown JSON object member can be extracted as:
|
||||
//
|
||||
// err := ...
|
||||
// var serr json.SemanticError
|
||||
// if errors.As(err, &serr) && serr.Err == json.ErrUnknownName {
|
||||
// serr, ok := errors.AsType[*json.SemanticError](err)
|
||||
// if ok && serr.Err == json.ErrUnknownName {
|
||||
// ptr := serr.JSONPointer // JSON pointer to unknown name
|
||||
// name := ptr.LastToken() // unknown name itself
|
||||
// ...
|
||||
@@ -38,6 +39,10 @@ import (
|
||||
// This error is only returned if [RejectUnknownMembers] is true.
|
||||
var ErrUnknownName = errors.New("unknown object member name")
|
||||
|
||||
// errAmbiguousName indicates a JSON object member could be unmarshal
|
||||
// into multiple candidate Go struct fields.
|
||||
var errAmbiguousName = errors.New("ambiguous object member name")
|
||||
|
||||
const errorPrefix = "json: "
|
||||
|
||||
func isSemanticError(err error) bool {
|
||||
@@ -50,7 +55,7 @@ func isSyntacticError(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// isFatalError reports whether this error must terminate asharling.
|
||||
// isFatalError reports whether this error must terminate arshaling.
|
||||
// All errors are considered fatal unless operating under
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] in which case only
|
||||
// syntactic errors and I/O errors are considered fatal.
|
||||
@@ -60,7 +65,12 @@ func isFatalError(err error, flags jsonflags.Flags) bool {
|
||||
}
|
||||
|
||||
// SemanticError describes an error determining the meaning
|
||||
// of JSON data as Go data or vice-versa.
|
||||
// of JSON data as Go data, or vice versa.
|
||||
//
|
||||
// If a [Marshaler], [MarshalerTo], [Unmarshaler], or [UnmarshalerFrom] method
|
||||
// returns a SemanticError when called by the [json] package,
|
||||
// then the ByteOffset, JSONPointer, and GoType fields are automatically
|
||||
// populated by the calling context if they are the zero value.
|
||||
//
|
||||
// The contents of this error as produced by this package may change over time.
|
||||
type SemanticError struct {
|
||||
@@ -69,7 +79,7 @@ type SemanticError struct {
|
||||
|
||||
action string // either "marshal" or "unmarshal"
|
||||
|
||||
// ByteOffset indicates that an error occurred after this byte offset.
|
||||
// ByteOffset indicates that an error occurred at or after this byte offset.
|
||||
ByteOffset int64
|
||||
// JSONPointer indicates that an error occurred within this JSON value
|
||||
// as indicated using the JSON Pointer notation (see RFC 6901).
|
||||
@@ -88,22 +98,25 @@ type SemanticError struct {
|
||||
}
|
||||
|
||||
// coder is implemented by [jsontext.Encoder] or [jsontext.Decoder].
|
||||
type coder interface{ StackPointer() jsontext.Pointer }
|
||||
type coder interface {
|
||||
StackPointer() jsontext.Pointer
|
||||
Options() Options
|
||||
}
|
||||
|
||||
// newInvalidFormatError wraps err in a SemanticError because
|
||||
// newInvalidFormatError constructs a SemanticError because
|
||||
// the current type t cannot handle the provided options format.
|
||||
// This error must be called before producing or consuming the next value.
|
||||
// This function must be called before producing or consuming the next value.
|
||||
//
|
||||
// If [jsonflags.ReportErrorsWithLegacySemantics] is specified,
|
||||
// then this automatically skips the next value when unmarshaling
|
||||
// to ensure that the value is fully consumed.
|
||||
func newInvalidFormatError(c coder, t reflect.Type, o *jsonopts.Struct) error {
|
||||
err := fmt.Errorf("invalid format flag %q", o.Format)
|
||||
func newInvalidFormatError(c coder, t reflect.Type) error {
|
||||
err := fmt.Errorf("invalid format flag %q", c.Options().(*jsonopts.Struct).Format)
|
||||
switch c := c.(type) {
|
||||
case *jsontext.Encoder:
|
||||
err = newMarshalErrorBefore(c, t, err)
|
||||
case *jsontext.Decoder:
|
||||
err = newUnmarshalErrorBeforeWithSkipping(c, o, t, err)
|
||||
err = newUnmarshalErrorBeforeWithSkipping(c, t, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -111,7 +124,7 @@ func newInvalidFormatError(c coder, t reflect.Type, o *jsonopts.Struct) error {
|
||||
// newMarshalErrorBefore wraps err in a SemanticError assuming that e
|
||||
// is positioned right before the next token or value, which causes an error.
|
||||
func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error {
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: err,
|
||||
return &SemanticError{action: "marshal", GoType: t, Err: toUnexpectedEOF(err),
|
||||
ByteOffset: e.OutputOffset() + int64(export.Encoder(e).CountNextDelimWhitespace()),
|
||||
JSONPointer: jsontext.Pointer(export.Encoder(e).AppendStackPointer(nil, +1))}
|
||||
}
|
||||
@@ -127,7 +140,7 @@ func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) err
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
k = d.PeekKind()
|
||||
}
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err,
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
|
||||
ByteOffset: d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1)),
|
||||
JSONKind: k}
|
||||
@@ -136,9 +149,9 @@ func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) err
|
||||
// newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
|
||||
// but automatically skips the next value if
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
|
||||
func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
|
||||
func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorBefore(d, t, err)
|
||||
if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := export.Decoder(d).SkipValue(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
@@ -150,13 +163,22 @@ func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct
|
||||
// is positioned right after the previous token or value, which caused an error.
|
||||
func newUnmarshalErrorAfter(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
tokOrVal := export.Decoder(d).PreviousTokenOrValue()
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: err,
|
||||
ByteOffset: d.InputOffset() - int64(len(tokOrVal)),
|
||||
byteOffset := d.InputOffset() - int64(len(tokOrVal))
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
// NOTE: In v1, the offset pointed to the end of the bad token or value.
|
||||
if k := jsontext.Value(tokOrVal).Kind(); k == '[' || k == '{' {
|
||||
byteOffset++ // add just the '[' or '{'
|
||||
} else {
|
||||
byteOffset += int64(len(tokOrVal))
|
||||
}
|
||||
}
|
||||
return &SemanticError{action: "unmarshal", GoType: t, Err: toUnexpectedEOF(err),
|
||||
ByteOffset: byteOffset,
|
||||
JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, -1)),
|
||||
JSONKind: jsontext.Value(tokOrVal).Kind()}
|
||||
}
|
||||
|
||||
// newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
|
||||
// newUnmarshalErrorAfterWithValue wraps err in a SemanticError assuming that d
|
||||
// is positioned right after the previous token or value, which caused an error.
|
||||
// It also stores a copy of the last JSON value if it is a string or number.
|
||||
func newUnmarshalErrorAfterWithValue(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
@@ -170,9 +192,9 @@ func newUnmarshalErrorAfterWithValue(d *jsontext.Decoder, t reflect.Type, err er
|
||||
// newUnmarshalErrorAfterWithSkipping is like [newUnmarshalErrorAfter],
|
||||
// but automatically skips the remainder of the current value if
|
||||
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
|
||||
func newUnmarshalErrorAfterWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
|
||||
func newUnmarshalErrorAfterWithSkipping(d *jsontext.Decoder, t reflect.Type, err error) error {
|
||||
err = newUnmarshalErrorAfter(d, t, err)
|
||||
if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
|
||||
if err2 := export.Decoder(d).SkipValueRemainder(); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
@@ -199,6 +221,7 @@ func newSemanticErrorWithPosition(c coder, t reflect.Type, prevDepth int, prevLe
|
||||
if serr == nil {
|
||||
serr = &SemanticError{Err: err}
|
||||
}
|
||||
serr.Err = toUnexpectedEOF(serr.Err)
|
||||
var currDepth int
|
||||
var currLength int64
|
||||
var coderState interface{ AppendStackPointer([]byte, int) []byte }
|
||||
@@ -289,6 +312,13 @@ func collapseSemanticErrors(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func wrapErrUnsupported(err error, what string) error {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
return errors.New(what + " may not return errors.ErrUnsupported")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// errorModalVerb is a modal verb like "cannot" or "unable to".
|
||||
//
|
||||
// Once per process, Hyrum-proof the error message by deliberately
|
||||
@@ -370,9 +400,9 @@ func (e *SemanticError) Error() string {
|
||||
}
|
||||
|
||||
// Special handling for unknown names.
|
||||
if e.Err == ErrUnknownName {
|
||||
if e.Err == ErrUnknownName || e.Err == errAmbiguousName {
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(ErrUnknownName.Error())
|
||||
sb.WriteString(e.Err.Error())
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(strconv.Quote(e.JSONPointer.LastToken()))
|
||||
if parent := e.JSONPointer.Parent(); parent != "" {
|
||||
@@ -425,3 +455,11 @@ func newDuplicateNameError(ptr jsontext.Pointer, quotedName []byte, offset int64
|
||||
Err: jsontext.ErrDuplicateName,
|
||||
}
|
||||
}
|
||||
|
||||
// toUnexpectedEOF converts [io.EOF] to [io.ErrUnexpectedEOF].
|
||||
func toUnexpectedEOF(err error) error {
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user