Skip to content

Commit

Permalink
Improve error positioning of SemanticError
Browse files Browse the repository at this point in the history
Changes made:
* Always populate SemanticError.JSONPointer.
* Export ErrUnknownName to programmatically identify an unknown name.
* Reduce verbosity of SemanticError.Error method.
* Apply Hyrum-proofing on a per-process basis,
  rather than on a per-error.Error method call.
* Cleanup error wrapping for user-provided method and function calls.
  Some heuristics were used to provided reasonable position injection
  into user-provided errors.
  • Loading branch information
dsnet committed Dec 13, 2024
1 parent 9084689 commit a37b36b
Show file tree
Hide file tree
Showing 21 changed files with 935 additions and 603 deletions.
5 changes: 3 additions & 2 deletions arshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ func UnmarshalDecode(in *jsontext.Decoder, out any, opts ...Options) (err error)
return unmarshalDecode(in, out, uo)
}

var errNonNilReference = errors.New("value must be passed as a non-nil pointer reference")

func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err error) {
v := reflect.ValueOf(out)
if !v.IsValid() || v.Kind() != reflect.Pointer || v.IsNil() {
Expand All @@ -438,8 +440,7 @@ func unmarshalDecode(in *jsontext.Decoder, out any, uo *jsonopts.Struct) (err er
t = t.Elem()
}
}
err := errors.New("value must be passed as a non-nil pointer reference")
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
return &SemanticError{action: "unmarshal", GoType: t, Err: errNonNilReference}
}
va := addressableValue{v.Elem()} // dereferenced pointer is always addressable
t := va.Type()
Expand Down
12 changes: 6 additions & 6 deletions arshal_any.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func unmarshalValueAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (any, error)
case '0':
fv, ok := jsonwire.ParseFloat(val, 64)
if !ok && uo.Flags.Get(jsonflags.RejectFloatOverflow) {
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: float64Type, Err: strconv.ErrRange}
return nil, newUnmarshalErrorAfter(dec, float64Type, strconv.ErrRange)
}
return fv, nil
default:
Expand All @@ -88,7 +88,7 @@ func marshalObjectAny(enc *jsontext.Encoder, obj map[string]any, mo *jsonopts.St
if xe.Tokens.Depth() > startDetectingCyclesAfter {
v := reflect.ValueOf(obj)
if err := visitPointer(&xe.SeenPointers, v); err != nil {
return err
return newMarshalErrorBefore(enc, anyType, err)
}
defer leavePointer(&xe.SeenPointers, v)
}
Expand Down Expand Up @@ -176,7 +176,7 @@ func unmarshalObjectAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (map[string]

// Manually check for duplicate names.
if _, ok := obj[name]; ok {
name := xd.PreviousBuffer()
name := xd.PreviousTokenOrValue()
err := newDuplicateNameError(dec.StackPointer(), nil, dec.InputOffset()-len64(name))
return obj, err
}
Expand All @@ -192,7 +192,7 @@ func unmarshalObjectAny(dec *jsontext.Decoder, uo *jsonopts.Struct) (map[string]
}
return obj, nil
}
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: mapStringAnyType}
return nil, newUnmarshalErrorAfter(dec, mapStringAnyType, nil)
}

func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) error {
Expand All @@ -201,7 +201,7 @@ func marshalArrayAny(enc *jsontext.Encoder, arr []any, mo *jsonopts.Struct) erro
if xe.Tokens.Depth() > startDetectingCyclesAfter {
v := reflect.ValueOf(arr)
if err := visitPointer(&xe.SeenPointers, v); err != nil {
return err
return newMarshalErrorBefore(enc, sliceAnyType, err)
}
defer leavePointer(&xe.SeenPointers, v)
}
Expand Down Expand Up @@ -259,5 +259,5 @@ func unmarshalArrayAny(dec *jsontext.Decoder, uo *jsonopts.Struct) ([]any, error
}
return arr, nil
}
return nil, &SemanticError{action: "unmarshal", JSONKind: k, GoType: sliceAnyType}
return nil, newUnmarshalErrorAfter(dec, sliceAnyType, nil)
}
Loading

0 comments on commit a37b36b

Please sign in to comment.