From baaeb5da4f5bd2b20fb8cd9fe331512840148946 Mon Sep 17 00:00:00 2001 From: Inteon <42113979+inteon@users.noreply.github.com> Date: Sun, 31 Oct 2021 12:03:46 +0100 Subject: [PATCH] consider 'unknown field' errors to be strict errors Signed-off-by: Inteon <42113979+inteon@users.noreply.github.com> --- decode.go | 13 ++++++++----- decode_test.go | 12 ++++++++---- yaml.go | 17 +++++++++-------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/decode.go b/decode.go index df36e3a3..8ccf115d 100644 --- a/decode.go +++ b/decode.go @@ -308,9 +308,10 @@ func (p *parser) mapping() *Node { // Decoder, unmarshals a node into a provided value. type decoder struct { - doc *Node - aliases map[*Node]bool - terrors []string + doc *Node + aliases map[*Node]bool + terrors []string + strictErrors []string stringMapType reflect.Type generalMapType reflect.Type @@ -361,6 +362,7 @@ func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { err := u.UnmarshalYAML(n) if e, ok := err.(*TypeError); ok { d.terrors = append(d.terrors, e.Errors...) + d.strictErrors = append(d.strictErrors, e.StrictErrors...) return false } if err != nil { @@ -377,12 +379,13 @@ func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good if len(d.terrors) > terrlen { issues := d.terrors[terrlen:] d.terrors = d.terrors[:terrlen] - return &TypeError{issues} + return &TypeError{issues, nil} } return nil }) if e, ok := err.(*TypeError); ok { d.terrors = append(d.terrors, e.Errors...) + d.strictErrors = append(d.strictErrors, e.StrictErrors...) return false } if err != nil { @@ -908,7 +911,7 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.unmarshal(n.Content[i+1], value) inlineMap.SetMapIndex(name, value) } else if d.knownFields { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) + d.strictErrors = append(d.strictErrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } return true diff --git a/decode_test.go b/decode_test.go index a5dfadca..5142d13b 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1110,8 +1110,8 @@ func (s *S) TestUnmarshalerWholeDocument(c *C) { } func (s *S) TestUnmarshalerTypeError(c *C) { - unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} - unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} + unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}, nil} + unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}, nil} defer func() { delete(unmarshalerResult, 2) delete(unmarshalerResult, 4) @@ -1141,8 +1141,8 @@ func (s *S) TestUnmarshalerTypeError(c *C) { } func (s *S) TestObsoleteUnmarshalerTypeError(c *C) { - unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}} - unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}} + unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}, nil} + unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}, nil} defer func() { delete(unmarshalerResult, 2) delete(unmarshalerResult, 4) @@ -1605,6 +1605,10 @@ func (s *S) TestUnmarshalKnownFields(c *C) { dec.KnownFields(item.known) err := dec.Decode(value.Interface()) c.Assert(err, ErrorMatches, item.error) + if !item.unique { + // If a strict error occurs, the decoding should still be successful + c.Assert(value.Elem().Interface(), DeepEquals, item.value) + } } } diff --git a/yaml.go b/yaml.go index 3ba221c2..0156aa61 100644 --- a/yaml.go +++ b/yaml.go @@ -129,8 +129,8 @@ func (dec *Decoder) Decode(v interface{}) (err error) { out = out.Elem() } d.unmarshal(node, out) - if len(d.terrors) > 0 { - return &TypeError{d.terrors} + if len(d.terrors) > 0 || len(d.strictErrors) > 0 { + return &TypeError{d.terrors, d.strictErrors} } return nil } @@ -147,8 +147,8 @@ func (n *Node) Decode(v interface{}) (err error) { out = out.Elem() } d.unmarshal(n, out) - if len(d.terrors) > 0 { - return &TypeError{d.terrors} + if len(d.terrors) > 0 || len(d.strictErrors) > 0 { + return &TypeError{d.terrors, d.strictErrors} } return nil } @@ -166,8 +166,8 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) { } d.unmarshal(node, v) } - if len(d.terrors) > 0 { - return &TypeError{d.terrors} + if len(d.terrors) > 0 || len(d.strictErrors) > 0 { + return &TypeError{d.terrors, d.strictErrors} } return nil } @@ -313,11 +313,12 @@ func failf(format string, args ...interface{}) { // types. When this error is returned, the value is still // unmarshaled partially. type TypeError struct { - Errors []string + Errors []string + StrictErrors []string } func (e *TypeError) Error() string { - return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(append(e.Errors, e.StrictErrors...), "\n ")) } type Kind uint32