Skip to content

Commit f62bb36

Browse files
committed
internal/encoding/gotypes: obey @go(,type=) for optional fields
The logic to make the generated Go type a pointer happened before we looked at a `@go()` attribute for a type= override, meaning that the pointer was generated on top. Instead, give the end user full control with type=. Signed-off-by: Daniel Martí <[email protected]> Change-Id: I5fe6336db0236fb8ee049a11cceda1b09ae1a806 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1207218 Reviewed-by: Roger Peppe <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent 1b787cd commit f62bb36

File tree

2 files changed

+21
-21
lines changed

2 files changed

+21
-21
lines changed

cmd/cue/cmd/testdata/script/exp_gengotypes.txtar

+4-4
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ func main() {
326326
var _ = zeroRoot.AttrChangedNameNested
327327
zeroRoot.AttrType = constant.Kind(0)
328328
zeroRoot.AttrTypeCompat = token.Token(0)
329-
// zeroRoot.AttrTypeNested = make(map[any]any)
329+
zeroRoot.AttrTypeNested = make(map[any]any)
330330
zeroRoot.Fields.OptionalStruct = &root.EmptyStruct{}
331-
// zeroRoot.Fields.OptionalStructAttrType = root.EmptyStruct{}
331+
zeroRoot.Fields.OptionalStructAttrType = root.EmptyStruct{}
332332

333333
// Sanity check that Go can JSON decode all the values we expect.
334334
// We also re-encode the ones we expect CUE to be able to validate again.
@@ -666,7 +666,7 @@ type Root struct {
666666

667667
OptionalStruct *EmptyStruct `json:"optionalStruct,omitempty"`
668668

669-
OptionalStructAttrType *EmptyStruct `json:"optionalStructAttrType,omitempty"`
669+
OptionalStructAttrType EmptyStruct `json:"optionalStructAttrType,omitempty"`
670670

671671
RegularStruct struct {
672672
Field int64 `json:"field,omitempty"`
@@ -688,7 +688,7 @@ type Root struct {
688688
// For compatibility with `cue get go`, an unnamed second value is also a type.
689689
AttrTypeCompat token.Token `json:"attrTypeCompat,omitempty"`
690690

691-
AttrTypeNested *map[any]any `json:"attrTypeNested,omitempty"`
691+
AttrTypeNested map[any]any `json:"attrTypeNested,omitempty"`
692692

693693
// doc is a great field.
694694
//

internal/encoding/gotypes/generate.go

+17-17
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func Generate(ctx *cue.Context, insts ...*build.Instance) error {
8383

8484
g.emitDocs(goName, val.Doc())
8585
g.appendf("type %s ", goName)
86-
if err := g.emitType(val); err != nil {
86+
if err := g.emitType(val, false); err != nil {
8787
return err
8888
}
8989
g.appendf("\n\n")
@@ -180,7 +180,7 @@ func (g *generator) addDef(path cue.Path) {
180180
// emitType generates a CUE value as a Go type.
181181
// When possible, the Go type is emitted in the form of a reference.
182182
// Otherwise, an inline Go type expression is used.
183-
func (g *generator) emitType(val cue.Value) error {
183+
func (g *generator) emitType(val cue.Value, optional bool) error {
184184
goAttr := val.Attribute("go")
185185
// We prefer the form @go(Name,type=pkg.Baz) as it is explicit and extensible,
186186
// but we are also backwards compatible with @go(Name,pkg.Baz) as emitted by `cue get go`.
@@ -201,6 +201,18 @@ func (g *generator) emitType(val cue.Value) error {
201201
g.appendf("%s", attrType)
202202
return nil
203203
}
204+
// TODO: should we ensure that optional fields are always nilable in Go?
205+
// On one hand this allows telling int64(0) apart from a missing field,
206+
// but on the other, it's often unnecessary and leads to clumsy types.
207+
// Perhaps add a @go() attribute parameter to require nullability.
208+
//
209+
// For now, only structs are always pointers when optional.
210+
// This is necessary to allow recursive Go types such as linked lists.
211+
// Pointers to structs are still OK in terms of UX, given that
212+
// one can do X.PtrY.Z without needing to do (*X.PtrY).Z.
213+
if optional && cue.Dereference(val).IncompleteKind() == cue.StructKind {
214+
g.appendf("*")
215+
}
204216
// TODO: support nullable types, such as `null | #SomeReference` and
205217
// `null | {foo: int}`.
206218
if g.emitTypeReference(val) {
@@ -210,7 +222,7 @@ func (g *generator) emitType(val cue.Value) error {
210222
case cue.StructKind:
211223
if elem := val.LookupPath(cue.MakePath(cue.AnyString)); elem.Err() == nil {
212224
g.appendf("map[string]")
213-
if err := g.emitType(elem); err != nil {
225+
if err := g.emitType(elem, false); err != nil {
214226
return err
215227
}
216228
break
@@ -242,19 +254,7 @@ func (g *generator) emitType(val cue.Value) error {
242254
}
243255

244256
g.appendf("%s ", goName)
245-
// TODO: should we ensure that optional fields are always nilable in Go?
246-
// On one hand this allows telling int64(0) apart from a missing field,
247-
// but on the other, it's often unnecessary and leads to clumsy types.
248-
// Perhaps add a @go() attribute parameter to require nullability.
249-
//
250-
// For now, only structs are always pointers when optional.
251-
// This is necessary to allow recursive Go types such as linked lists.
252-
// Pointers to structs are still OK in terms of UX, given that
253-
// one can do X.PtrY.Z without needing to do (*X.PtrY).Z.
254-
if optional && cue.Dereference(val).IncompleteKind() == cue.StructKind {
255-
g.appendf("*")
256-
}
257-
if err := g.emitType(val); err != nil {
257+
if err := g.emitType(val, optional); err != nil {
258258
return err
259259
}
260260
// TODO: should we generate cuego tags like `cue:"expr"`?
@@ -275,7 +275,7 @@ func (g *generator) emitType(val cue.Value) error {
275275
if !elem.Exists() {
276276
// TODO: perhaps mention the original type.
277277
g.appendf("any /* CUE closed list */")
278-
} else if err := g.emitType(elem); err != nil {
278+
} else if err := g.emitType(elem, false); err != nil {
279279
return err
280280
}
281281

0 commit comments

Comments
 (0)