Source file src/encoding/json/jsontext/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 jsontext
     8  
     9  import (
    10  	"bytes"
    11  	"io"
    12  	"strconv"
    13  
    14  	"encoding/json/internal/jsonflags"
    15  	"encoding/json/internal/jsonopts"
    16  	"encoding/json/internal/jsonwire"
    17  )
    18  
    19  const errorPrefix = "jsontext: "
    20  
    21  type ioError struct {
    22  	action string // either "read" or "write"
    23  	err    error
    24  }
    25  
    26  func (e *ioError) Error() string {
    27  	return errorPrefix + e.action + " error: " + e.err.Error()
    28  }
    29  func (e *ioError) Unwrap() error {
    30  	return e.err
    31  }
    32  
    33  type numError struct {
    34  	accessor string // either "Int", "Uint", or "Float"
    35  	value    string // e.g., "1e1000"
    36  	err      error  // either [strconv.ErrSyntax] or [strconv.ErrRange]
    37  }
    38  
    39  func (e *numError) Error() string {
    40  	return "jsontext.Token(" + e.value + ")." + e.accessor + " error: " + e.err.Error()
    41  }
    42  func (e *numError) Unwrap() error {
    43  	return e.err
    44  }
    45  
    46  // SyntacticError is a description of an error that occurred when
    47  // encoding or decoding JSON according to the grammar.
    48  //
    49  // The contents of this error as produced by this package may change over time.
    50  type SyntacticError struct {
    51  	requireKeyedLiterals
    52  	nonComparable
    53  
    54  	// ByteOffset indicates that an error occurred at or after this byte offset.
    55  	ByteOffset int64
    56  	// JSONPointer indicates that an error occurred within this JSON value
    57  	// as indicated using the JSON Pointer notation (see RFC 6901).
    58  	JSONPointer Pointer
    59  
    60  	// Err is the underlying error.
    61  	Err error
    62  }
    63  
    64  // wrapSyntacticError wraps an error and annotates it with a precise location
    65  // using the provided [encoderState] or [decoderState].
    66  // If err is an [ioError] or [io.EOF], then it is not wrapped.
    67  //
    68  // It takes a relative offset pos that can be resolved into
    69  // an absolute offset using state.offsetAt.
    70  //
    71  // It takes a where that specifies how the JSON pointer is derived.
    72  // If the underlying error is a [pointerSuffixError],
    73  // then the suffix is appended to the derived pointer.
    74  func wrapSyntacticError(state interface {
    75  	options() *jsonopts.Struct
    76  	offsetAt(pos int) int64
    77  	AppendStackPointer(b []byte, where int) []byte
    78  }, err error, pos, where int) error {
    79  	if _, ok := err.(*ioError); err == io.EOF || ok {
    80  		return err
    81  	}
    82  	offset := state.offsetAt(pos)
    83  	ptr := state.AppendStackPointer(nil, where)
    84  	if serr, ok := err.(*pointerSuffixError); ok {
    85  		ptr = serr.appendPointer(ptr)
    86  		err = serr.error
    87  	}
    88  	if d, ok := state.(*decoderState); ok && err == errMismatchDelim {
    89  		where := "at start of value"
    90  		if len(d.Tokens.Stack) > 0 && d.Tokens.Last.Length() > 0 {
    91  			switch {
    92  			case d.Tokens.Last.isArray():
    93  				where = "after array element (expecting ',' or ']')"
    94  				ptr = []byte(Pointer(ptr).Parent()) // problem is with parent array
    95  			case d.Tokens.Last.isObject():
    96  				where = "after object value (expecting ',' or '}')"
    97  				ptr = []byte(Pointer(ptr).Parent()) // problem is with parent object
    98  			}
    99  		}
   100  		err = jsonwire.NewInvalidCharacterError(d.buf[pos:], where)
   101  	}
   102  	if state.options().Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) && err != io.ErrUnexpectedEOF {
   103  		// The v1 error offset more consistently reported
   104  		// the number of bytes read that lead to the error.
   105  		// This implies that the offset is after the invalid text.
   106  		if werr, ok := err.(*jsonwire.InvalidTextError); ok && werr != nil {
   107  			offset += int64(len(werr.What))
   108  		} else {
   109  			// This implies a structural error (e.g., parsing a JSON number
   110  			// when the grammar is expecting a JSON string for the object name).
   111  			// In such a case, simply increment by one, since the first byte
   112  			// is usually sufficient to indicate that the incoming token
   113  			// cannot possibly be correct.
   114  			offset++
   115  		}
   116  	}
   117  	return &SyntacticError{ByteOffset: offset, JSONPointer: Pointer(ptr), Err: err}
   118  }
   119  
   120  func (e *SyntacticError) Error() string {
   121  	pointer := e.JSONPointer
   122  	offset := e.ByteOffset
   123  	b := []byte(errorPrefix)
   124  	if e.Err != nil {
   125  		b = append(b, e.Err.Error()...)
   126  		if e.Err == ErrDuplicateName {
   127  			b = strconv.AppendQuote(append(b, ' '), pointer.LastToken())
   128  			pointer = pointer.Parent()
   129  			offset = 0 // not useful to print offset for duplicate names
   130  		}
   131  	} else {
   132  		b = append(b, "syntactic error"...)
   133  	}
   134  	if pointer != "" {
   135  		b = strconv.AppendQuote(append(b, " within "...), jsonwire.TruncatePointer(string(pointer), 100))
   136  	}
   137  	if offset > 0 {
   138  		b = strconv.AppendInt(append(b, " after offset "...), offset, 10)
   139  	}
   140  	return string(b)
   141  }
   142  
   143  func (e *SyntacticError) Unwrap() error {
   144  	return e.Err
   145  }
   146  
   147  // pointerSuffixError represents a JSON pointer suffix to be appended
   148  // to [SyntacticError.JSONPointer]. It is an internal error type
   149  // used within this package and does not appear in the public API.
   150  //
   151  // This type is primarily used to annotate errors in Encoder.WriteValue
   152  // and Decoder.ReadValue with precise positions.
   153  // At the time WriteValue or ReadValue is called, a JSON pointer to the
   154  // upcoming value can be constructed using the Encoder/Decoder state.
   155  // However, tracking pointers within values during normal operation
   156  // would incur a performance penalty in the error-free case.
   157  //
   158  // To provide precise error locations without this overhead,
   159  // the error is wrapped with object names or array indices
   160  // as the call stack is popped when an error occurs.
   161  // Since this happens in reverse order, pointerSuffixError holds
   162  // the pointer in reverse and is only later reversed when appending to
   163  // the pointer prefix.
   164  //
   165  // For example, if the encoder is at "/alpha/bravo/charlie"
   166  // and an error occurs in WriteValue at "/xray/yankee/zulu", then
   167  // the final pointer should be "/alpha/bravo/charlie/xray/yankee/zulu".
   168  //
   169  // As pointerSuffixError is populated during the error return path,
   170  // it first contains "/zulu", then "/zulu/yankee",
   171  // and finally "/zulu/yankee/xray".
   172  // These tokens are reversed and concatenated to "/alpha/bravo/charlie"
   173  // to form the full pointer.
   174  type pointerSuffixError struct {
   175  	error
   176  
   177  	// reversePointer is a JSON pointer, but with each token in reverse order.
   178  	reversePointer []byte
   179  }
   180  
   181  // wrapWithObjectName wraps err with a JSON object name access,
   182  // which must be a valid quoted JSON string.
   183  func wrapWithObjectName(err error, quotedName []byte) error {
   184  	serr, _ := err.(*pointerSuffixError)
   185  	if serr == nil {
   186  		serr = &pointerSuffixError{error: err}
   187  	}
   188  	name := jsonwire.UnquoteMayCopy(quotedName, false)
   189  	serr.reversePointer = appendEscapePointerName(append(serr.reversePointer, '/'), name)
   190  	return serr
   191  }
   192  
   193  // wrapWithArrayIndex wraps err with a JSON array index access.
   194  func wrapWithArrayIndex(err error, index int64) error {
   195  	serr, _ := err.(*pointerSuffixError)
   196  	if serr == nil {
   197  		serr = &pointerSuffixError{error: err}
   198  	}
   199  	serr.reversePointer = strconv.AppendUint(append(serr.reversePointer, '/'), uint64(index), 10)
   200  	return serr
   201  }
   202  
   203  // appendPointer appends the path encoded in e to the end of pointer.
   204  func (e *pointerSuffixError) appendPointer(pointer []byte) []byte {
   205  	// Copy each token in reversePointer to the end of pointer in reverse order.
   206  	// Double reversal means that the appended suffix is now in forward order.
   207  	bi, bo := e.reversePointer, pointer
   208  	for len(bi) > 0 {
   209  		i := bytes.LastIndexByte(bi, '/')
   210  		bi, bo = bi[:i], append(bo, bi[i:]...)
   211  	}
   212  	return bo
   213  }
   214  

View as plain text