This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+60 -22
View File
@@ -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
}