Source file src/encoding/json/v2/errors.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.jsonv2
     6  
     7  package json
     8  
     9  import (
    10  	"cmp"
    11  	"errors"
    12  	"fmt"
    13  	"reflect"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  
    18  	"encoding/json/internal/jsonflags"
    19  	"encoding/json/internal/jsonopts"
    20  	"encoding/json/internal/jsonwire"
    21  	"encoding/json/jsontext"
    22  )
    23  
    24  // ErrUnknownName indicates that a JSON object member could not be
    25  // unmarshaled because the name is not known to the target Go struct.
    26  // This error is directly wrapped within a [SemanticError] when produced.
    27  //
    28  // The name of an unknown JSON object member can be extracted as:
    29  //
    30  //	err := ...
    31  //	var serr json.SemanticError
    32  //	if errors.As(err, &serr) && serr.Err == json.ErrUnknownName {
    33  //		ptr := serr.JSONPointer // JSON pointer to unknown name
    34  //		name := ptr.LastToken() // unknown name itself
    35  //		...
    36  //	}
    37  //
    38  // This error is only returned if [RejectUnknownMembers] is true.
    39  var ErrUnknownName = errors.New("unknown object member name")
    40  
    41  const errorPrefix = "json: "
    42  
    43  func isSemanticError(err error) bool {
    44  	_, ok := err.(*SemanticError)
    45  	return ok
    46  }
    47  
    48  func isSyntacticError(err error) bool {
    49  	_, ok := err.(*jsontext.SyntacticError)
    50  	return ok
    51  }
    52  
    53  // isFatalError reports whether this error must terminate asharling.
    54  // All errors are considered fatal unless operating under
    55  // [jsonflags.ReportErrorsWithLegacySemantics] in which case only
    56  // syntactic errors and I/O errors are considered fatal.
    57  func isFatalError(err error, flags jsonflags.Flags) bool {
    58  	return !flags.Get(jsonflags.ReportErrorsWithLegacySemantics) ||
    59  		isSyntacticError(err) || export.IsIOError(err)
    60  }
    61  
    62  // SemanticError describes an error determining the meaning
    63  // of JSON data as Go data or vice-versa.
    64  //
    65  // The contents of this error as produced by this package may change over time.
    66  type SemanticError struct {
    67  	requireKeyedLiterals
    68  	nonComparable
    69  
    70  	action string // either "marshal" or "unmarshal"
    71  
    72  	// ByteOffset indicates that an error occurred after this byte offset.
    73  	ByteOffset int64
    74  	// JSONPointer indicates that an error occurred within this JSON value
    75  	// as indicated using the JSON Pointer notation (see RFC 6901).
    76  	JSONPointer jsontext.Pointer
    77  
    78  	// JSONKind is the JSON kind that could not be handled.
    79  	JSONKind jsontext.Kind // may be zero if unknown
    80  	// JSONValue is the JSON number or string that could not be unmarshaled.
    81  	// It is not populated during marshaling.
    82  	JSONValue jsontext.Value // may be nil if irrelevant or unknown
    83  	// GoType is the Go type that could not be handled.
    84  	GoType reflect.Type // may be nil if unknown
    85  
    86  	// Err is the underlying error.
    87  	Err error // may be nil
    88  }
    89  
    90  // coder is implemented by [jsontext.Encoder] or [jsontext.Decoder].
    91  type coder interface{ StackPointer() jsontext.Pointer }
    92  
    93  // newInvalidFormatError wraps err in a SemanticError because
    94  // the current type t cannot handle the provided options format.
    95  // This error must be called before producing or consuming the next value.
    96  //
    97  // If [jsonflags.ReportErrorsWithLegacySemantics] is specified,
    98  // then this automatically skips the next value when unmarshaling
    99  // to ensure that the value is fully consumed.
   100  func newInvalidFormatError(c coder, t reflect.Type, o *jsonopts.Struct) error {
   101  	err := fmt.Errorf("invalid format flag %q", o.Format)
   102  	switch c := c.(type) {
   103  	case *jsontext.Encoder:
   104  		err = newMarshalErrorBefore(c, t, err)
   105  	case *jsontext.Decoder:
   106  		err = newUnmarshalErrorBeforeWithSkipping(c, o, t, err)
   107  	}
   108  	return err
   109  }
   110  
   111  // newMarshalErrorBefore wraps err in a SemanticError assuming that e
   112  // is positioned right before the next token or value, which causes an error.
   113  func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error {
   114  	return &SemanticError{action: "marshal", GoType: t, Err: err,
   115  		ByteOffset:  e.OutputOffset() + int64(export.Encoder(e).CountNextDelimWhitespace()),
   116  		JSONPointer: jsontext.Pointer(export.Encoder(e).AppendStackPointer(nil, +1))}
   117  }
   118  
   119  // newUnmarshalErrorBefore wraps err in a SemanticError assuming that d
   120  // is positioned right before the next token or value, which causes an error.
   121  // It does not record the next JSON kind as this error is used to indicate
   122  // the receiving Go value is invalid to unmarshal into (and not a JSON error).
   123  // However, if [jsonflags.ReportErrorsWithLegacySemantics] is specified,
   124  // then it does record the next JSON kind for historical reporting reasons.
   125  func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) error {
   126  	var k jsontext.Kind
   127  	if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   128  		k = d.PeekKind()
   129  	}
   130  	return &SemanticError{action: "unmarshal", GoType: t, Err: err,
   131  		ByteOffset:  d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
   132  		JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1)),
   133  		JSONKind:    k}
   134  }
   135  
   136  // newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
   137  // but automatically skips the next value if
   138  // [jsonflags.ReportErrorsWithLegacySemantics] is specified.
   139  func newUnmarshalErrorBeforeWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
   140  	err = newUnmarshalErrorBefore(d, t, err)
   141  	if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   142  		if err2 := export.Decoder(d).SkipValue(); err2 != nil {
   143  			return err2
   144  		}
   145  	}
   146  	return err
   147  }
   148  
   149  // newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
   150  // is positioned right after the previous token or value, which caused an error.
   151  func newUnmarshalErrorAfter(d *jsontext.Decoder, t reflect.Type, err error) error {
   152  	tokOrVal := export.Decoder(d).PreviousTokenOrValue()
   153  	return &SemanticError{action: "unmarshal", GoType: t, Err: err,
   154  		ByteOffset:  d.InputOffset() - int64(len(tokOrVal)),
   155  		JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, -1)),
   156  		JSONKind:    jsontext.Value(tokOrVal).Kind()}
   157  }
   158  
   159  // newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
   160  // is positioned right after the previous token or value, which caused an error.
   161  // It also stores a copy of the last JSON value if it is a string or number.
   162  func newUnmarshalErrorAfterWithValue(d *jsontext.Decoder, t reflect.Type, err error) error {
   163  	serr := newUnmarshalErrorAfter(d, t, err).(*SemanticError)
   164  	if serr.JSONKind == '"' || serr.JSONKind == '0' {
   165  		serr.JSONValue = jsontext.Value(export.Decoder(d).PreviousTokenOrValue()).Clone()
   166  	}
   167  	return serr
   168  }
   169  
   170  // newUnmarshalErrorAfterWithSkipping is like [newUnmarshalErrorAfter],
   171  // but automatically skips the remainder of the current value if
   172  // [jsonflags.ReportErrorsWithLegacySemantics] is specified.
   173  func newUnmarshalErrorAfterWithSkipping(d *jsontext.Decoder, o *jsonopts.Struct, t reflect.Type, err error) error {
   174  	err = newUnmarshalErrorAfter(d, t, err)
   175  	if o.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
   176  		if err2 := export.Decoder(d).SkipValueRemainder(); err2 != nil {
   177  			return err2
   178  		}
   179  	}
   180  	return err
   181  }
   182  
   183  // newSemanticErrorWithPosition wraps err in a SemanticError assuming that
   184  // the error occurred at the provided depth, and length.
   185  // If err is already a SemanticError, then position information is only
   186  // injected if it is currently unpopulated.
   187  //
   188  // If the position is unpopulated, it is ambiguous where the error occurred
   189  // in the user code, whether it was before or after the current position.
   190  // For the byte offset, we assume that the error occurred before the last read
   191  // token or value when decoding, or before the next value when encoding.
   192  // For the JSON pointer, we point to the parent object or array unless
   193  // we can be certain that it happened with an object member.
   194  //
   195  // This is used to annotate errors returned by user-provided
   196  // v2 MarshalJSON or UnmarshalJSON methods or functions.
   197  func newSemanticErrorWithPosition(c coder, t reflect.Type, prevDepth int, prevLength int64, err error) error {
   198  	serr, _ := err.(*SemanticError)
   199  	if serr == nil {
   200  		serr = &SemanticError{Err: err}
   201  	}
   202  	var currDepth int
   203  	var currLength int64
   204  	var coderState interface{ AppendStackPointer([]byte, int) []byte }
   205  	var offset int64
   206  	switch c := c.(type) {
   207  	case *jsontext.Encoder:
   208  		e := export.Encoder(c)
   209  		serr.action = cmp.Or(serr.action, "marshal")
   210  		currDepth, currLength = e.Tokens.DepthLength()
   211  		offset = c.OutputOffset() + int64(export.Encoder(c).CountNextDelimWhitespace())
   212  		coderState = e
   213  	case *jsontext.Decoder:
   214  		d := export.Decoder(c)
   215  		serr.action = cmp.Or(serr.action, "unmarshal")
   216  		currDepth, currLength = d.Tokens.DepthLength()
   217  		tokOrVal := d.PreviousTokenOrValue()
   218  		offset = c.InputOffset() - int64(len(tokOrVal))
   219  		if (prevDepth == currDepth && prevLength == currLength) || len(tokOrVal) == 0 {
   220  			// If no Read method was called in the user-defined method or
   221  			// if the Peek method was called, then use the offset of the next value.
   222  			offset = c.InputOffset() + int64(export.Decoder(c).CountNextDelimWhitespace())
   223  		}
   224  		coderState = d
   225  	}
   226  	serr.ByteOffset = cmp.Or(serr.ByteOffset, offset)
   227  	if serr.JSONPointer == "" {
   228  		where := 0 // default to ambiguous positioning
   229  		switch {
   230  		case prevDepth == currDepth && prevLength+0 == currLength:
   231  			where = +1
   232  		case prevDepth == currDepth && prevLength+1 == currLength:
   233  			where = -1
   234  		}
   235  		serr.JSONPointer = jsontext.Pointer(coderState.AppendStackPointer(nil, where))
   236  	}
   237  	serr.GoType = cmp.Or(serr.GoType, t)
   238  	return serr
   239  }
   240  
   241  // collapseSemanticErrors collapses double SemanticErrors at the outer levels
   242  // into a single SemanticError by preserving the inner error,
   243  // but prepending the ByteOffset and JSONPointer with the outer error.
   244  //
   245  // For example:
   246  //
   247  //	collapseSemanticErrors(&SemanticError{
   248  //		ByteOffset:  len64(`[0,{"alpha":[0,1,`),
   249  //		JSONPointer: "/1/alpha/2",
   250  //		GoType:      reflect.TypeFor[outerType](),
   251  //		Err: &SemanticError{
   252  //			ByteOffset:  len64(`{"foo":"bar","fizz":[0,`),
   253  //			JSONPointer: "/fizz/1",
   254  //			GoType:      reflect.TypeFor[innerType](),
   255  //			Err:         ...,
   256  //		},
   257  //	})
   258  //
   259  // results in:
   260  //
   261  //	&SemanticError{
   262  //		ByteOffset:  len64(`[0,{"alpha":[0,1,`) + len64(`{"foo":"bar","fizz":[0,`),
   263  //		JSONPointer: "/1/alpha/2" + "/fizz/1",
   264  //		GoType:      reflect.TypeFor[innerType](),
   265  //		Err:         ...,
   266  //	}
   267  //
   268  // This is used to annotate errors returned by user-provided
   269  // v1 MarshalJSON or UnmarshalJSON methods with precise position information
   270  // if they themselves happened to return a SemanticError.
   271  // Since MarshalJSON and UnmarshalJSON are not operating on the root JSON value,
   272  // their positioning must be relative to the nested JSON value
   273  // returned by UnmarshalJSON or passed to MarshalJSON.
   274  // Therefore, we can construct an absolute position by concatenating
   275  // the outer with the inner positions.
   276  //
   277  // Note that we do not use collapseSemanticErrors with user-provided functions
   278  // that take in an [jsontext.Encoder] or [jsontext.Decoder] since they contain
   279  // methods to report position relative to the root JSON value.
   280  // We assume user-constructed errors are correctly precise about position.
   281  func collapseSemanticErrors(err error) error {
   282  	if serr1, ok := err.(*SemanticError); ok {
   283  		if serr2, ok := serr1.Err.(*SemanticError); ok {
   284  			serr2.ByteOffset = serr1.ByteOffset + serr2.ByteOffset
   285  			serr2.JSONPointer = serr1.JSONPointer + serr2.JSONPointer
   286  			*serr1 = *serr2
   287  		}
   288  	}
   289  	return err
   290  }
   291  
   292  // errorModalVerb is a modal verb like "cannot" or "unable to".
   293  //
   294  // Once per process, Hyrum-proof the error message by deliberately
   295  // switching between equivalent renderings of the same error message.
   296  // The randomization is tied to the Hyrum-proofing already applied
   297  // on map iteration in Go.
   298  var errorModalVerb = sync.OnceValue(func() string {
   299  	for phrase := range map[string]struct{}{"cannot": {}, "unable to": {}} {
   300  		return phrase // use whichever phrase we get in the first iteration
   301  	}
   302  	return ""
   303  })
   304  
   305  func (e *SemanticError) Error() string {
   306  	var sb strings.Builder
   307  	sb.WriteString(errorPrefix)
   308  	sb.WriteString(errorModalVerb())
   309  
   310  	// Format action.
   311  	var preposition string
   312  	switch e.action {
   313  	case "marshal":
   314  		sb.WriteString(" marshal")
   315  		preposition = " from"
   316  	case "unmarshal":
   317  		sb.WriteString(" unmarshal")
   318  		preposition = " into"
   319  	default:
   320  		sb.WriteString(" handle")
   321  		preposition = " with"
   322  	}
   323  
   324  	// Format JSON kind.
   325  	switch e.JSONKind {
   326  	case 'n':
   327  		sb.WriteString(" JSON null")
   328  	case 'f', 't':
   329  		sb.WriteString(" JSON boolean")
   330  	case '"':
   331  		sb.WriteString(" JSON string")
   332  	case '0':
   333  		sb.WriteString(" JSON number")
   334  	case '{', '}':
   335  		sb.WriteString(" JSON object")
   336  	case '[', ']':
   337  		sb.WriteString(" JSON array")
   338  	default:
   339  		if e.action == "" {
   340  			preposition = ""
   341  		}
   342  	}
   343  	if len(e.JSONValue) > 0 && len(e.JSONValue) < 100 {
   344  		sb.WriteByte(' ')
   345  		sb.Write(e.JSONValue)
   346  	}
   347  
   348  	// Format Go type.
   349  	if e.GoType != nil {
   350  		typeString := e.GoType.String()
   351  		if len(typeString) > 100 {
   352  			// An excessively long type string most likely occurs for
   353  			// an anonymous struct declaration with many fields.
   354  			// Reduce the noise by just printing the kind,
   355  			// and optionally prepending it with the package name
   356  			// if the struct happens to include an unexported field.
   357  			typeString = e.GoType.Kind().String()
   358  			if e.GoType.Kind() == reflect.Struct && e.GoType.Name() == "" {
   359  				for i := range e.GoType.NumField() {
   360  					if pkgPath := e.GoType.Field(i).PkgPath; pkgPath != "" {
   361  						typeString = pkgPath[strings.LastIndexByte(pkgPath, '/')+len("/"):] + ".struct"
   362  						break
   363  					}
   364  				}
   365  			}
   366  		}
   367  		sb.WriteString(preposition)
   368  		sb.WriteString(" Go ")
   369  		sb.WriteString(typeString)
   370  	}
   371  
   372  	// Special handling for unknown names.
   373  	if e.Err == ErrUnknownName {
   374  		sb.WriteString(": ")
   375  		sb.WriteString(ErrUnknownName.Error())
   376  		sb.WriteString(" ")
   377  		sb.WriteString(strconv.Quote(e.JSONPointer.LastToken()))
   378  		if parent := e.JSONPointer.Parent(); parent != "" {
   379  			sb.WriteString(" within ")
   380  			sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(parent), 100)))
   381  		}
   382  		return sb.String()
   383  	}
   384  
   385  	// Format where.
   386  	// Avoid printing if it overlaps with a wrapped SyntacticError.
   387  	switch serr, _ := e.Err.(*jsontext.SyntacticError); {
   388  	case e.JSONPointer != "":
   389  		if serr == nil || !e.JSONPointer.Contains(serr.JSONPointer) {
   390  			sb.WriteString(" within ")
   391  			sb.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(e.JSONPointer), 100)))
   392  		}
   393  	case e.ByteOffset > 0:
   394  		if serr == nil || !(e.ByteOffset <= serr.ByteOffset) {
   395  			sb.WriteString(" after offset ")
   396  			sb.WriteString(strconv.FormatInt(e.ByteOffset, 10))
   397  		}
   398  	}
   399  
   400  	// Format underlying error.
   401  	if e.Err != nil {
   402  		errString := e.Err.Error()
   403  		if isSyntacticError(e.Err) {
   404  			errString = strings.TrimPrefix(errString, "jsontext: ")
   405  		}
   406  		sb.WriteString(": ")
   407  		sb.WriteString(errString)
   408  	}
   409  
   410  	return sb.String()
   411  }
   412  
   413  func (e *SemanticError) Unwrap() error {
   414  	return e.Err
   415  }
   416  
   417  func newDuplicateNameError(ptr jsontext.Pointer, quotedName []byte, offset int64) error {
   418  	if quotedName != nil {
   419  		name, _ := jsonwire.AppendUnquote(nil, quotedName)
   420  		ptr = ptr.AppendToken(string(name))
   421  	}
   422  	return &jsontext.SyntacticError{
   423  		ByteOffset:  offset,
   424  		JSONPointer: ptr,
   425  		Err:         jsontext.ErrDuplicateName,
   426  	}
   427  }
   428  

View as plain text