Skip to content

Commit

Permalink
core/convert: Correct conversion of array types
Browse files Browse the repository at this point in the history
The existing conversion used the old multiplication of lists when
converting Go Array types to CUE. E.g. [3]string{} -> [string] * 3.
This was deprecated years ago, and removed in issue #2237 and
https://review.gerrithub.io/c/cue-lang/cue/+/1200221

Instead, we now convert [3]string{} -> list.Repeat([string], 3)

Signed-off-by: Matthew Sackman <[email protected]>
Change-Id: I49aa552a5d7cb04ba5279b10d54d6b4804747f0e
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1207907
Reviewed-by: Daniel Martí <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
  • Loading branch information
cuematthew committed Jan 28, 2025
1 parent e9ad52f commit 8dc16dd
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 37 deletions.
10 changes: 7 additions & 3 deletions internal/core/convert/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,13 @@ func (c *goConverter) goTypeToValueRec(allowNullDefault bool, t reflect.Type) (e
}

if t.Kind() == reflect.Array {
e = ast.NewBinExpr(token.MUL,
ast.NewLit(token.INT, strconv.Itoa(t.Len())),
ast.NewList(elem))
e = ast.NewCall(
ast.NewSel(&ast.Ident{
Name: "list",
Node: ast.NewImport(nil, "list")},
"Repeat"),
ast.NewList(elem),
ast.NewLit(token.INT, strconv.Itoa(t.Len())))
} else {
e = ast.NewList(&ast.Ellipsis{Type: elem})
}
Expand Down
88 changes: 54 additions & 34 deletions internal/core/convert/go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"cuelang.org/go/internal/core/convert"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/runtime"

_ "cuelang.org/go/pkg"
)

func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
Expand Down Expand Up @@ -265,26 +267,27 @@ func TestX(t *testing.T) {

func TestConvertType(t *testing.T) {
testCases := []struct {
goTyp interface{}
want string
goTyp interface{}
want string
expectError bool
}{{
struct {
goTyp: struct {
A int `cue:">=0&<100"`
B *big.Int `cue:">=0"`
C *big.Int
D big.Int
F *big.Float
}{},
// TODO: indicate that B is explicitly an int only.
`{
want: `{
A: (((int & >=-9223372036854775808) & <=9223372036854775807) & (>=0 & <100))
B: (int & >=0)
C?: int
D: int
F?: number
}`,
}, {
&struct {
goTyp: &struct {
A int16 `cue:">=0&<100"`
B error `json:"b,"`
C string
Expand All @@ -294,7 +297,7 @@ func TestConvertType(t *testing.T) {
T time.Time
G func()
}{},
`(*null|{
want: `(*null|{
A: (((int & >=-32768) & <=32767) & (>=0 & <100))
b: null
C: string
Expand All @@ -304,83 +307,89 @@ func TestConvertType(t *testing.T) {
T: _
})`,
}, {
struct {
goTyp: struct {
A int `cue:"<"` // invalid
}{},
"_|_(invalid tag \"<\" for field \"A\": expected operand, found 'EOF' (and 1 more errors))",
want: "_|_(invalid tag \"<\" for field \"A\": expected operand, found 'EOF' (and 1 more errors))",
expectError: true,
}, {
struct {
goTyp: struct {
A int `json:"-"` // skip
D *apd.Decimal
P ***apd.Decimal
I interface{ Foo() }
T string `cue:""` // allowed
h int
}{},
`{
want: `{
D?: number
P?: (*null|number)
I?: _
T: (string & _)
}`,
}, {
struct {
goTyp: struct {
A int8 `cue:"C-B"`
B int8 `cue:"C-A,opt"`
C int8 `cue:"A+B"`
}{},
// TODO: should B be marked as optional?
`{
want: `{
A: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;B〉))
B?: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;A〉))
C: (((int & >=-128) & <=127) & (〈0;A〉 + 〈0;B〉))
}`,
}, {
[]string{},
`(*null|[
goTyp: []string{},
want: `(*null|[
...string,
])`,
}, {
[4]string{},
`(4 * [
goTyp: [4]string{},
want: `〈import;list〉.Repeat([
string,
])`,
], 4)`,
}, {
[]func(){},
"_|_(unsupported Go type (func()))",
goTyp: []func(){},
want: "_|_(unsupported Go type (func()))",
expectError: true,
}, {
map[string]struct{ A map[string]uint }{},
`(*null|{
goTyp: map[string]struct{ A map[string]uint }{},
want: `(*null|{
[string]: {
A?: (*null|{
[string]: ((int & >=0) & <=18446744073709551615)
})
}
})`,
}, {
map[float32]int{},
`_|_(unsupported Go type for map key (float32))`,
goTyp: map[float32]int{},
want: `_|_(unsupported Go type for map key (float32))`,
expectError: true,
}, {
map[int]map[float32]int{},
`_|_(unsupported Go type for map key (float32))`,
goTyp: map[int]map[float32]int{},
want: `_|_(unsupported Go type for map key (float32))`,
expectError: true,
}, {
map[int]func(){},
`_|_(unsupported Go type (func()))`,
goTyp: map[int]func(){},
want: `_|_(unsupported Go type (func()))`,
expectError: true,
}, {
time.Now, // a function
"_|_(unsupported Go type (func() time.Time))",
goTyp: time.Now, // a function
want: "_|_(unsupported Go type (func() time.Time))",
expectError: true,
}, {
struct {
goTyp: struct {
Foobar string `cue:"\"foo,bar\",opt"`
}{},
`{
want: `{
Foobar?: (string & "foo,bar")
}`,
}, {
struct {
goTyp: struct {
Foobar string `cue:"\"foo,opt,bar\""`
}{},
`{
want: `{
Foobar: (string & "foo,opt,bar")
}`,
}}
Expand All @@ -390,11 +399,22 @@ func TestConvertType(t *testing.T) {
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
ctx := adt.NewContext(r, &adt.Vertex{})
v, _ := convert.GoTypeToExpr(ctx, tc.goTyp)
v, err := convert.GoTypeToExpr(ctx, tc.goTyp)
got := debug.NodeString(ctx, v, nil)
if got != tc.want {
t.Errorf("\n got %q;\nwant %q", got, tc.want)
}
if tc.expectError && err == nil {
t.Errorf("\n expected an error but didn't get one")
} else if !tc.expectError && err != nil {
t.Errorf("\n got unexpected error: %v", err)
}
if err == nil && !tc.expectError {
val, _ := ctx.Evaluate(&adt.Environment{}, v)
if bot, ok := val.(*adt.Bottom); ok {
t.Errorf("\n unexpected error when evaluating result of conversion: %v", bot)
}
}
})
}
}

0 comments on commit 8dc16dd

Please sign in to comment.