From: Joe Tsai Date: Tue, 9 Sep 2025 00:33:47 +0000 (-0700) Subject: encoding/json/v2: document context annotation with SemanticError X-Git-Tag: go1.26rc1~916 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=30686c4cc89e6952ec00846e34016f37d53f31dd;p=gostls13.git encoding/json/v2: document context annotation with SemanticError When the json package calls Marshaler, MarshalerTo, Unmarshaler, or UnmarshalerFrom methods and a SemanticError is returned, it will automatically annotate the error with context. Document this behavior. Change-Id: Id8e775a7c1c2a6ffc29ea518913591915e8aff87 Reviewed-on: https://go-review.googlesource.com/c/go/+/701956 Reviewed-by: Damien Neil Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI --- diff --git a/src/encoding/json/v2/arshal_methods.go b/src/encoding/json/v2/arshal_methods.go index 664c3927cc..2decd144db 100644 --- a/src/encoding/json/v2/arshal_methods.go +++ b/src/encoding/json/v2/arshal_methods.go @@ -41,6 +41,10 @@ var ( // // It is recommended that implementations return a buffer that is safe // for the caller to retain and potentially mutate. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError]. type Marshaler interface { MarshalJSON() ([]byte, error) } @@ -54,6 +58,11 @@ type Marshaler interface { // // The implementation must write only one JSON value to the Encoder and // must not retain the pointer to [jsontext.Encoder]. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError], +// unless it is an IO error. type MarshalerTo interface { MarshalJSONTo(*jsontext.Encoder) error @@ -72,6 +81,10 @@ type MarshalerTo interface { // unmarshaling into a pre-populated value. // // Implementations must not retain or mutate the input []byte. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError]. type Unmarshaler interface { UnmarshalJSON([]byte) error } @@ -88,6 +101,11 @@ type Unmarshaler interface { // unmarshaling into a pre-populated value. // // Implementations must not retain the pointer to [jsontext.Decoder]. +// +// If the returned error is a [SemanticError], then unpopulated fields +// of the error may be populated by [json] with additional context. +// Errors of other types are wrapped within a [SemanticError], +// unless it is a [jsontext.SyntacticError] or an IO error. type UnmarshalerFrom interface { UnmarshalJSONFrom(*jsontext.Decoder) error diff --git a/src/encoding/json/v2/errors.go b/src/encoding/json/v2/errors.go index 940b720210..9485d7b527 100644 --- a/src/encoding/json/v2/errors.go +++ b/src/encoding/json/v2/errors.go @@ -62,6 +62,11 @@ func isFatalError(err error, flags jsonflags.Flags) bool { // SemanticError describes an error determining the meaning // 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 { requireKeyedLiterals diff --git a/src/encoding/json/v2/example_orderedobject_test.go b/src/encoding/json/v2/example_orderedobject_test.go index d68782f725..fc23132504 100644 --- a/src/encoding/json/v2/example_orderedobject_test.go +++ b/src/encoding/json/v2/example_orderedobject_test.go @@ -53,7 +53,9 @@ func (obj *OrderedObject[V]) MarshalJSONTo(enc *jsontext.Encoder) error { // UnmarshalJSONFrom decodes a JSON object from dec into obj. func (obj *OrderedObject[V]) UnmarshalJSONFrom(dec *jsontext.Decoder) error { if k := dec.PeekKind(); k != '{' { - return fmt.Errorf("expected object start, but encountered %v", k) + // The [json] package automatically populates relevant fields + // in a [json.SemanticError] to provide additional context. + return &json.SemanticError{JSONKind: k} } if _, err := dec.ReadToken(); err != nil { return err