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

View as plain text