Skip to content

Commit cf18d6f

Browse files
committed
encoding/toml: support decoding keys named "_"
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]>
1 parent e8e6f04 commit cf18d6f

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

encoding/toml/decode.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -338,21 +338,13 @@ func decodeKey(key rootedKey, iter toml.Iterator) (rootedKey, []string) {
338338
// The "leaf" field, in this case "zzz", leaves its value as nil to be filled out.
339339
func inlineFields(names []string, relPos token.RelPos) (top, leaf *ast.Field) {
340340
curField := &ast.Field{
341-
Label: &ast.Ident{
342-
// We let the caller configure whether the first field starts a new line,
343-
// because that's not desirable for inline tables.
344-
NamePos: token.NoPos.WithRel(relPos),
345-
Name: names[0],
346-
},
341+
Label: label(names[0], token.NoPos.WithRel(relPos)),
347342
}
348343

349344
topField := curField
350345
for _, elem := range names[1:] {
351346
nextField := &ast.Field{
352-
Label: &ast.Ident{
353-
NamePos: token.NoPos.WithRel(token.Blank), // on the same line
354-
Name: elem,
355-
},
347+
Label: label(elem, token.NoPos.WithRel(token.Blank)), // on the same line
356348
}
357349
curField.Value = &ast.StructLit{Elts: []ast.Decl{nextField}}
358350
curField = nextField
@@ -370,6 +362,24 @@ func quoteLabelIfNeeded(name string) string {
370362
return literal.Label.Quote(name)
371363
}
372364

365+
// label creates an ast.Label that represents a key with exactly the literal string name.
366+
// This means a quoted string literal for the key "_", as TOML never means "top",
367+
// as well as for any keys beginning with an underscore, as we don't want to hide any fields.
368+
// cue/format knows how to quote any other identifiers correctly.
369+
func label(name string, pos token.Pos) ast.Label {
370+
if strings.HasPrefix(name, "_") {
371+
return &ast.BasicLit{
372+
ValuePos: pos,
373+
Kind: token.STRING,
374+
Value: literal.String.Quote(name),
375+
}
376+
}
377+
return &ast.Ident{
378+
NamePos: pos,
379+
Name: name,
380+
}
381+
}
382+
373383
// decodeExpr decodes a single TOML value expression, found on the right side
374384
// of a `key = value` line.
375385
func (d *Decoder) decodeExpr(key rootedKey, tnode *toml.Node) (ast.Expr, error) {

encoding/toml/decode_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,19 @@ func TestDecoder(t *testing.T) {
9191
name: "RootKeysCharacters",
9292
input: `
9393
a-b = "dashes"
94-
a_b = "underscores"
94+
a_b = "underscore unquoted"
95+
_ = "underscore quoted"
96+
_ab = "underscore prefix quoted"
9597
123 = "numbers"
98+
x._.y._ = "underscores quoted"
9699
`,
97100
wantCUE: `
98101
"a-b": "dashes"
99-
a_b: "underscores"
102+
a_b: "underscore unquoted"
103+
"_": "underscore quoted"
104+
"_ab": "underscore prefix quoted"
100105
"123": "numbers"
106+
x: "_": y: "_": "underscores quoted"
101107
`,
102108
}, {
103109
name: "RootKeysQuoted",

0 commit comments

Comments
 (0)