Skip to content

Commit

Permalink
encoding/toml: support decoding keys named "_"
Browse files Browse the repository at this point in the history
We can't blindly create an ast.Ident with such a name, as that then
refers to the "top" identifier in CUE, which cannot be used as a label:

    foo: cannot use _ as label:

We hadn't noticed this with other keys which need quoting such as "a-b"
because sticking the unquoted string in ast.Ident.Name caused cue/format
to quote the identifier correctly. It does not do this for "_" as it is
a valid identifier - it just represents top, and not a literal string.

Fixes #3350.

Signed-off-by: Daniel Martí <[email protected]>
Change-Id: If0f2c2fd6c0d984a7c6c30e4667ab8ed097aa17b
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1198819
Unity-Result: CUE porcuepine <[email protected]>
Reviewed-by: Roger Peppe <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
  • Loading branch information
mvdan committed Aug 6, 2024
1 parent e8e6f04 commit cf18d6f
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 12 deletions.
30 changes: 20 additions & 10 deletions encoding/toml/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,21 +338,13 @@ func decodeKey(key rootedKey, iter toml.Iterator) (rootedKey, []string) {
// The "leaf" field, in this case "zzz", leaves its value as nil to be filled out.
func inlineFields(names []string, relPos token.RelPos) (top, leaf *ast.Field) {
curField := &ast.Field{
Label: &ast.Ident{
// We let the caller configure whether the first field starts a new line,
// because that's not desirable for inline tables.
NamePos: token.NoPos.WithRel(relPos),
Name: names[0],
},
Label: label(names[0], token.NoPos.WithRel(relPos)),
}

topField := curField
for _, elem := range names[1:] {
nextField := &ast.Field{
Label: &ast.Ident{
NamePos: token.NoPos.WithRel(token.Blank), // on the same line
Name: elem,
},
Label: label(elem, token.NoPos.WithRel(token.Blank)), // on the same line
}
curField.Value = &ast.StructLit{Elts: []ast.Decl{nextField}}
curField = nextField
Expand All @@ -370,6 +362,24 @@ func quoteLabelIfNeeded(name string) string {
return literal.Label.Quote(name)
}

// label creates an ast.Label that represents a key with exactly the literal string name.
// This means a quoted string literal for the key "_", as TOML never means "top",
// as well as for any keys beginning with an underscore, as we don't want to hide any fields.
// cue/format knows how to quote any other identifiers correctly.
func label(name string, pos token.Pos) ast.Label {
if strings.HasPrefix(name, "_") {
return &ast.BasicLit{
ValuePos: pos,
Kind: token.STRING,
Value: literal.String.Quote(name),
}
}
return &ast.Ident{
NamePos: pos,
Name: name,
}
}

// decodeExpr decodes a single TOML value expression, found on the right side
// of a `key = value` line.
func (d *Decoder) decodeExpr(key rootedKey, tnode *toml.Node) (ast.Expr, error) {
Expand Down
10 changes: 8 additions & 2 deletions encoding/toml/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,19 @@ func TestDecoder(t *testing.T) {
name: "RootKeysCharacters",
input: `
a-b = "dashes"
a_b = "underscores"
a_b = "underscore unquoted"
_ = "underscore quoted"
_ab = "underscore prefix quoted"
123 = "numbers"
x._.y._ = "underscores quoted"
`,
wantCUE: `
"a-b": "dashes"
a_b: "underscores"
a_b: "underscore unquoted"
"_": "underscore quoted"
"_ab": "underscore prefix quoted"
"123": "numbers"
x: "_": y: "_": "underscores quoted"
`,
}, {
name: "RootKeysQuoted",
Expand Down

0 comments on commit cf18d6f

Please sign in to comment.