Skip to content

Commit 375c1b4

Browse files
author
sam boyer
committed
thema: Additional assignability checks
1 parent 80c1181 commit 375c1b4

File tree

2 files changed

+42
-12
lines changed

2 files changed

+42
-12
lines changed

assignable.go

+15-12
Original file line numberDiff line numberDiff line change
@@ -88,29 +88,28 @@ func assignable(sch cue.Value, T interface{}) error {
8888

8989
check = func(gval, sval cue.Value, p cue.Path) {
9090
sk, gk := sval.IncompleteKind(), gval.IncompleteKind()
91-
schemaHadNull, goHadNull := sk&cue.NullKind != 0, gk&cue.NullKind != 0
91+
schemaHadNull := sk&cue.NullKind != 0
9292
sk &^= cue.NullKind
9393
gk &^= cue.NullKind
9494

9595
ogval := gval
96-
if goHadNull {
97-
// At least for now, we have to deal with these unhelpful *null
98-
// appearing in the encoding of pointer types.
99-
//
100-
// We can't do the same for the schema side, because this null stripper
101-
// relies on the fact that all actual Go type declarations will come across
102-
// as a single value, without any disjunctions.
103-
gval = stripLeadNull(gval)
104-
}
96+
// At least for now, we have to deal with these unhelpful *null
97+
// appearing in the encoding of pointer types.
98+
//
99+
// We can't do the same for the schema side, because this null stripper
100+
// relies on the fact that all actual Go type declarations will come across
101+
// as a single value, without any disjunctions.
102+
gval = stripLeadNull(gval)
105103

106104
// strict equality _might_ be too restrictive? But it's better to start there
107-
if sk != gk && gk != cue.TopKind {
105+
if sk != gk && gk != (cue.TopKind^cue.NullKind) {
108106
errs[p.String()] = fmt.Errorf("%s: is kind %s in schema, but kind %s in Go type", p, sk, gk)
109107
return
110-
} else if gk == cue.TopKind {
108+
} else if gk == (cue.TopKind ^ cue.NullKind) {
111109
// Escape hatch for a Go interface{}/any
112110
return
113111
}
112+
op, _ := sval.Expr()
114113

115114
switch sk {
116115
case cue.ListKind:
@@ -122,6 +121,10 @@ func assignable(sch cue.Value, T interface{}) error {
122121
checkscalar(gval, sval, p)
123122
}
124123
case cue.StructKind:
124+
if op == cue.OrOp {
125+
errs[p.String()] = fmt.Errorf("%s: contains disjunction over struct types, but Go type is not any", p)
126+
return
127+
}
125128
checkstruct(gval, sval, p)
126129
case cue.NullKind:
127130
errs[p.String()] = fmt.Errorf("%s: null is not permitted in schema; express optionality with ?", p)

assignable_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,33 @@ func TestAssignable(t *testing.T) {
4747
}
4848
`,
4949
},
50+
"any": {
51+
T: &struct {
52+
AString any `json:"aString"`
53+
AnInt any `json:"anInt"`
54+
}{},
55+
cue: `typ: {
56+
aString: string
57+
anInt: int32
58+
}
59+
`,
60+
},
61+
"not-any-union": {
62+
T: &struct {
63+
Hopeful struct {
64+
AString any `json:"aString"`
65+
} `json:"hopeful"`
66+
}{},
67+
cue: `typ: {
68+
hopeful: {
69+
aString: string
70+
} | {
71+
anInt: int32
72+
}
73+
}
74+
`,
75+
invalid: true,
76+
},
5077
"stringEnumNoPointer": {
5178
T: struct {
5279
Foo string `json:"foo"`

0 commit comments

Comments
 (0)