Skip to content

Commit

Permalink
internal/encoding/yaml: reject trailing input in Unmarshal
Browse files Browse the repository at this point in the history
To be consistent with encoding/json.Unmarshal, and so that both
pkg/encoding/json and pkg/encoding/yaml's Unmarshal APIs reject it.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: Iee7dabf35d578d461c7aecd5ff6282774a51d017
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1193242
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
  • Loading branch information
mvdan committed Apr 18, 2024
1 parent e063155 commit 25af2e8
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 14 deletions.
14 changes: 11 additions & 3 deletions internal/encoding/yaml/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,23 @@ func (d *decoder) Decode() (ast.Expr, error) {
// Unmarshal parses a single YAML value to a CUE expression.
func Unmarshal(filename string, data []byte) (ast.Expr, error) {
d := NewDecoder(filename, data)
x, err := d.Decode()
n, err := d.Decode()
if err != nil {
if err == io.EOF {
return nil, nil // empty input
}
return nil, err
}
// TODO(mvdan): fail if there are more documents or garbage in the input
return x, nil
// TODO(mvdan): decoding the entire next value is unnecessary;
// consider either a "More" or "Done" method to tell if we are at EOF,
// or splitting the Decode method into two variants.
// This should use proper error values with positions as well.
if n2, err := d.Decode(); err == nil {
return nil, fmt.Errorf("%s: expected a single YAML document", n2.Pos())
} else if err != io.EOF {
return nil, fmt.Errorf("expected a single YAML document: %v", err)
}
return n, nil
}

func (d *decoder) extract(yn *yaml.Node) (ast.Expr, error) {
Expand Down
36 changes: 27 additions & 9 deletions internal/encoding/yaml/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ import (
"strings"
"testing"

"github.com/go-quicktest/qt"
"github.com/google/go-cmp/cmp"

"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
"cuelang.org/go/internal"
"cuelang.org/go/internal/cueexperiment"
"cuelang.org/go/internal/cuetest"
"cuelang.org/go/internal/encoding/yaml"
"github.com/google/go-cmp/cmp"
)

// These tests are only for the new YAML decoder.
Expand Down Expand Up @@ -764,13 +766,6 @@ a:
"Override anchor": "Bar"
"Reuse anchor": "Bar"`,
},
// Single document with garbage following it.
// TODO(mvdan): This should work for a single Decoder.Decode call,
// but it should be an error with Unmarshal.
{
"---\nhello\n...\n}not yaml",
`"hello"`,
},
}

type M map[interface{}]interface{}
Expand All @@ -785,7 +780,7 @@ func cueStr(node ast.Node) string {

func newDecoder(t *testing.T, data string) yaml.Decoder {
t.Helper()
t.Logf("input yaml:\n%s", data)
t.Logf(" yaml:\n%s", data)
return yaml.NewDecoder("test.yaml", []byte(data))
}

Expand Down Expand Up @@ -978,3 +973,26 @@ func TestFiles(t *testing.T) {
})
}
}

func TestTrailingInput(t *testing.T) {
for _, input := range []string{
"---\nfirst\n...\n}invalid yaml",
"---\nfirst\n---\nsecond\n",
} {
t.Run("", func(t *testing.T) {
// Unmarshal should fail as it expects one value.
wantErr := ".*expected a single YAML document.*"
_, err := callUnmarshal(t, input)
qt.Assert(t, qt.ErrorMatches(err, wantErr))

// A single Decode call should succeed, no matter whether there is any valid or invalid trailing input.
wantFirst := `"first"`
dec := newDecoder(t, input)
expr, err := dec.Decode()
qt.Assert(t, qt.IsNil(err))
gotFirst := cueStr(expr)
qt.Assert(t, qt.Equals(gotFirst, wantFirst))
})
}

}
8 changes: 6 additions & 2 deletions pkg/encoding/yaml/testdata/gen.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ t4: error in call to encoding/yaml.ValidatePartial: invalid value 4 (out of boun
./in.cue:6:5
./in.cue:6:49
yaml.ValidatePartial:3:4
unmarshalTrailingInput.valid: error in call to encoding/yaml.Unmarshal: 3:1: expected a single YAML document:
./in.cue:27:11
unmarshalTrailingInput.invalid: error in call to encoding/yaml.Unmarshal: expected a single YAML document: :2: did not find expected node content:
./in.cue:28:11

Result:
t1: _|_ // t1: error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3)
Expand Down Expand Up @@ -81,6 +85,6 @@ unmarshalStream: {
null2: [1, null, 2]
}
unmarshalTrailingInput: {
valid: 1
invalid: 1
valid: _|_ // unmarshalTrailingInput.valid: error in call to encoding/yaml.Unmarshal: 3:1: expected a single YAML document
invalid: _|_ // unmarshalTrailingInput.invalid: error in call to encoding/yaml.Unmarshal: expected a single YAML document: :2: did not find expected node content
}

0 comments on commit 25af2e8

Please sign in to comment.