Make error position tracking explicit with Offset field on ParserError
Thread byte offset information through all error creation sites, eliminating the need for SubsliceOffset to recover position from pointer comparison. Changes: - Add Offset field to ParserError struct - Add offset parameter to NewParserError - Add Parser.offsetOf helper for suffix-length arithmetic - Thread base offset through scanner functions (scanComment, scanBasicString, scanMultilineBasicString, scanLiteralString, scanMultilineLiteralString, scanWindowsNewline) - Thread base offset through standalone functions (expect, hexToRune) - Thread base offset through all decode functions (parseInteger, parseFloat, parseLocalDate, parseLocalTime, parseLocalDateTime, parseDateTime, checkAndRemoveUnderscores*) - Update all unmarshaler call sites to pass value.Raw.Offset - Update localtime.go UnmarshalText methods with base=0 - Update strict.go to populate Offset from key ranges - Change wrapDecodeError to read de.Offset directly - Change Utf8TomlValidAlreadyEscaped to return int index (-1 if valid) instead of a byte subslice - Unexport SubsliceOffset (now only used internally by Range()) This makes error positions self-describing: each ParserError carries its own byte offset, so callers no longer need the original document slice and address arithmetic to determine where an error occurred. Co-authored-by: Thomas Pelletier <thomas@pelletier.dev>
This commit is contained in:
+20
-20
@@ -625,7 +625,7 @@ func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.V
|
||||
}
|
||||
}
|
||||
}
|
||||
return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice")
|
||||
return reflect.Value{}, unstable.NewParserError(key.Node().Data, int(key.Node().Raw.Offset), "cannot store a table in a slice")
|
||||
}
|
||||
if key.Next() {
|
||||
// Still scoping the key
|
||||
@@ -748,7 +748,7 @@ func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool
|
||||
if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) {
|
||||
err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
|
||||
if err != nil {
|
||||
return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err)
|
||||
return false, unstable.NewParserError(d.p.Raw(node.Raw), int(node.Raw.Offset), "%w", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
@@ -896,7 +896,7 @@ func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) e
|
||||
}
|
||||
return d.unmarshalInlineTable(itable, elem)
|
||||
default:
|
||||
return unstable.NewParserError(d.p.Raw(itable.Raw), "cannot store inline table in Go type %s", v.Kind())
|
||||
return unstable.NewParserError(d.p.Raw(itable.Raw), int(itable.Raw.Offset), "cannot store inline table in Go type %s", v.Kind())
|
||||
}
|
||||
|
||||
it := itable.Children()
|
||||
@@ -916,26 +916,26 @@ func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) e
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error {
|
||||
dt, err := parseDateTime(value.Data)
|
||||
dt, err := parseDateTime(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("datetime", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("datetime", v.Type()))
|
||||
}
|
||||
v.Set(reflect.ValueOf(dt))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error {
|
||||
ld, err := parseLocalDate(value.Data)
|
||||
ld, err := parseLocalDate(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local date", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("local date", v.Type()))
|
||||
}
|
||||
if v.Type() == timeType {
|
||||
v.Set(reflect.ValueOf(ld.AsTime(time.Local)))
|
||||
@@ -946,34 +946,34 @@ func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) erro
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error {
|
||||
lt, rest, err := parseLocalTime(value.Data)
|
||||
lt, rest, err := parseLocalTime(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(rest) > 0 {
|
||||
return unstable.NewParserError(rest, "extra characters at the end of a local time")
|
||||
return unstable.NewParserError(rest, int(value.Raw.Offset)+len(value.Data)-len(rest), "extra characters at the end of a local time")
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Interface {
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local time", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("local time", v.Type()))
|
||||
}
|
||||
v.Set(reflect.ValueOf(lt))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error {
|
||||
ldt, rest, err := parseLocalDateTime(value.Data)
|
||||
ldt, rest, err := parseLocalDateTime(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(rest) > 0 {
|
||||
return unstable.NewParserError(rest, "extra characters at the end of a local date time")
|
||||
return unstable.NewParserError(rest, int(value.Raw.Offset)+len(value.Data)-len(rest), "extra characters at the end of a local date time")
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local datetime", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("local datetime", v.Type()))
|
||||
}
|
||||
if v.Type() == timeType {
|
||||
v.Set(reflect.ValueOf(ldt.AsTime(time.Local)))
|
||||
@@ -992,14 +992,14 @@ func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error {
|
||||
case reflect.Interface:
|
||||
v.Set(reflect.ValueOf(b))
|
||||
default:
|
||||
return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b)
|
||||
return unstable.NewParserError(value.Data, int(value.Raw.Offset), "cannot assign boolean to a %t", b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error {
|
||||
f, err := parseFloat(value.Data)
|
||||
f, err := parseFloat(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1009,13 +1009,13 @@ func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error {
|
||||
v.SetFloat(f)
|
||||
case reflect.Float32:
|
||||
if f > math.MaxFloat32 {
|
||||
return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f)
|
||||
return unstable.NewParserError(value.Data, int(value.Raw.Offset), "number %f does not fit in a float32", f)
|
||||
}
|
||||
v.SetFloat(f)
|
||||
case reflect.Interface:
|
||||
v.Set(reflect.ValueOf(f))
|
||||
default:
|
||||
return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind())
|
||||
return unstable.NewParserError(value.Data, int(value.Raw.Offset), "float cannot be assigned to %s", v.Kind())
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1048,7 +1048,7 @@ func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error
|
||||
return d.unmarshalFloat(value, v)
|
||||
}
|
||||
|
||||
i, err := parseInteger(value.Data)
|
||||
i, err := parseInteger(value.Data, int(value.Raw.Offset))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1116,7 +1116,7 @@ func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error
|
||||
case reflect.Interface:
|
||||
r = reflect.ValueOf(i)
|
||||
default:
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("integer", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("integer", v.Type()))
|
||||
}
|
||||
|
||||
if !r.Type().AssignableTo(v.Type()) {
|
||||
@@ -1135,7 +1135,7 @@ func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error {
|
||||
case reflect.Interface:
|
||||
v.Set(reflect.ValueOf(string(value.Data)))
|
||||
default:
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("string", v.Type()))
|
||||
return unstable.NewParserError(d.p.Raw(value.Raw), int(value.Raw.Offset), "%s", d.typeMismatchString("string", v.Type()))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user