Skip to content

Commit d1306ff

Browse files
authored
checker,json2: relax checking of x.enum = integer at comptime; refactor json2 to clean it up (#16926)
1 parent 09f4845 commit d1306ff

File tree

9 files changed

+89
-24
lines changed

9 files changed

+89
-24
lines changed

vlib/v/checker/assign.v

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,16 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
593593
node.pos)
594594
}
595595
} else {
596-
c.error('cannot assign to `${left}`: ${err.msg()}', right.pos())
596+
// allow `t.$(field.name) = 0` where `t.$(field.name)` is a enum
597+
if c.inside_comptime_for_field && left is ast.ComptimeSelector {
598+
field_sym := c.table.sym(c.unwrap_generic(c.comptime_fields_default_type))
599+
600+
if field_sym.kind == .enum_ && !right_type.is_int() {
601+
c.error('enums can only be assigned `int` values', right.pos())
602+
}
603+
} else {
604+
c.error('cannot assign to `${left}`: ${err.msg()}', right.pos())
605+
}
597606
}
598607
}
599608
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
vlib/v/checker/tests/assign_enum_at_comptime.vv:13:21: error: enums can only be assigned `int` values
2+
11 |
3+
12 | $for field in TestStruct.fields {
4+
13 | t.$(field.name) = '1'
5+
| ~~~
6+
14 | }
7+
15 | }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
enum TestEnum {
2+
one = 1
3+
}
4+
5+
struct TestStruct {
6+
test TestEnum
7+
}
8+
9+
fn main() {
10+
mut t := TestStruct{}
11+
12+
$for field in TestStruct.fields {
13+
t.$(field.name) = '1'
14+
}
15+
}

vlib/v/tests/comptime_field_selector_test.v

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ mut:
55
name string
66
}
77

8+
enum TestEnum {
9+
one = 1
10+
}
11+
12+
struct TestStruct {
13+
test TestEnum
14+
}
15+
816
fn comptime_field_selector_read[T]() []string {
917
mut t := T{}
1018
t.name = '2'
@@ -42,6 +50,15 @@ fn test_comptime_field_selector_write() {
4250
assert res.name == '1'
4351
}
4452

53+
fn test_comptime_field_selector_write_enum() {
54+
mut t := TestStruct{}
55+
56+
$for field in TestStruct.fields {
57+
t.$(field.name) = 1
58+
}
59+
assert t.test == .one
60+
}
61+
4562
struct Foo2 {
4663
f Foo
4764
}

vlib/x/json2/encode_struct_test.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ fn test_types() {
6363
val: 1
6464
}
6565
}) == '{"val":{"val":1}}'
66+
67+
assert json.encode(StructType[Enumerates]{}) == '{"val":0}'
68+
assert json.encode(StructType[Enumerates]{ val: Enumerates.a }) == '{"val":0}'
69+
assert json.encode(StructType[Enumerates]{ val: Enumerates.d }) == '{"val":3}'
70+
assert json.encode(StructType[Enumerates]{ val: Enumerates.e }) == '{"val":99}'
71+
assert json.encode(StructType[Enumerates]{ val: Enumerates.f }) == '{"val":100}'
6672
}
6773

6874
fn test_option_types() {

vlib/x/json2/encoder.v

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
207207
e.encode_string(parsed_time.format_rfc3339(), mut wr)!
208208
} $else $if field.is_array {
209209
e.encode_array(value, level + 1, mut wr)!
210+
} $else $if field.is_enum {
211+
option_value := val.$(field.name) as ?int
212+
wr.write(Any(option_value).int().str().bytes())!
210213
} $else $if field.is_alias {
211214
match field.unaliased_typ {
212215
typeof[string]().idx {
@@ -253,6 +256,8 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! {
253256
e.encode_array(value, level + 1, mut wr)!
254257
} $else $if field.is_struct {
255258
e.encode_struct(value, level + 1, mut wr)!
259+
} $else $if field.is_enum {
260+
wr.write(int(val.$(field.name)).str().bytes())!
256261
} $else $if field.is_alias {
257262
match field.unaliased_typ {
258263
typeof[string]().idx {

vlib/x/json2/json2.v

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ pub fn decode[T](src string) !T {
5151
typ.$(field.name) = res[field.name]!.str()
5252
} $else $if field.typ is time.Time {
5353
typ.$(field.name) = res[field.name]!.to_time()!
54+
} $else $if field.is_array {
55+
// typ.$(field.name) = res[field.name]!.arr()
56+
} $else $if field.is_struct {
57+
} $else $if field.is_enum {
58+
typ.$(field.name) = res[field.name]!.int()
59+
} $else $if field.is_alias {
60+
} $else $if field.is_map {
5461
} $else {
5562
return error("The type of `${field.name}` can't be decoded. Please open an issue at https://github.com/vlang/v/issues/new/choose")
5663
}

vlib/x/json2/json_module_compatibility_test/json_test.v

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@ enum JobTitle {
99

1010
struct Employee {
1111
pub mut:
12-
name string
13-
age int
14-
salary f32
15-
// title JobTitle //! FIXME - decode
16-
// sub_employee SubEmployee //! FIXME - decode
12+
name string
13+
age int
14+
salary f32
15+
title JobTitle
16+
sub_employee SubEmployee //! FIXME - decode
1717
}
1818

19-
struct SubEmployee {
19+
pub struct SubEmployee {
2020
pub mut:
2121
name string
2222
age int
2323
salary f32
24-
// title JobTitle //! FIXME - decode
24+
title JobTitle
2525
}
2626

2727
fn test_simple() {
2828
sub_employee := SubEmployee{
2929
name: 'João'
3030
}
31-
x := Employee{'Peter', 28, 95000.5}
31+
x := Employee{'Peter', 28, 95000.5, .worker, sub_employee}
3232
s := json.encode[Employee](x)
33-
assert s == '{"name":"Peter","age":28,"salary":95000.5}'
33+
assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0.0,"title":0}}'
3434

3535
y := json.decode[Employee](s) or {
3636
println(err)
@@ -40,10 +40,10 @@ fn test_simple() {
4040
assert y.name == 'Peter'
4141
assert y.age == 28
4242
assert y.salary == 95000.5
43-
// assert y.title == .worker //! FIXME
44-
// assert y.sub_employee.name == 'Peter'
45-
// assert y.sub_employee.age == 0
46-
// assert y.sub_employee.salary == 0.0
43+
assert y.title == .worker
44+
// assert y.sub_employee.name == 'João'
45+
assert y.sub_employee.age == 0
46+
assert y.sub_employee.salary == 0.0
4747
// assert y.sub_employee.title == .worker //! FIXME
4848
}
4949

vlib/x/json2/json_module_compatibility_test/json_todo_test.vv

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct Price {
2525
//! BUGFIX
2626
fn test_field_with_default_expr() {
2727
data := '[{"net":1},{"net":2,"currencyId":"cjson"}]'
28-
prices := json.decode<[]Price>(data)!
28+
prices := json.decode[[]Price](data)!
2929
assert prices == [Price{
3030
net: 1
3131
currency_id: 'cconst'
@@ -38,7 +38,7 @@ fn test_field_with_default_expr() {
3838
//! BUGFIX - .from_json(res)
3939
fn test_decode_top_level_array() {
4040
s := '[{"name":"Peter", "age": 29}, {"name":"Bob", "age":31}]'
41-
x := json.decode<[]Employee>(s) or { panic(err) }
41+
x := json.decode[[]Employee](s) or { panic(err) }
4242
assert x.len == 2
4343
assert x[0].name == 'Peter'
4444
assert x[0].age == 29
@@ -86,7 +86,7 @@ fn test_encode_decode_sumtype() {
8686

8787
assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"Time","value":${t.unix_time()}}]}'
8888

89-
dec := json.decode<SomeGame>(enc)!
89+
dec := json.decode[SomeGame](enc)!
9090

9191
assert game.title == dec.title
9292
assert game.player == dec.player
@@ -114,8 +114,8 @@ pub mut:
114114

115115
fn test_parse_user() {
116116
s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}'
117-
u2 := json.decode<User2>(s)!
118-
u := json.decode<User>(s)!
117+
u2 := json.decode[User2](s)!
118+
u := json.decode[User](s)!
119119
assert u.age == 10
120120
assert u.last_name == 'Johnson'
121121
assert u.is_registered == true
@@ -135,7 +135,7 @@ fn test_encode_decode_time() {
135135
}
136136
s := json.encode(user)
137137
assert s.contains('"reg_date":1608621780')
138-
user2 := json.decode<User2>(s)!
138+
user2 := json.decode[User2](s)!
139139
assert user2.reg_date.str() == '2020-12-22 07:23:00'
140140
}
141141

@@ -150,7 +150,7 @@ struct Country {
150150

151151
//! BUGFIX - .from_json(res)
152152
fn test_struct_in_struct() {
153-
country := json.decode<Country>('{ "name": "UK", "cities": [{"name":"London"}, {"name":"Manchester"}]}')!
153+
country := json.decode[Country]('{ "name": "UK", "cities": [{"name":"London"}, {"name":"Manchester"}]}')!
154154
assert country.name == 'UK'
155155
assert country.cities.len == 2
156156
assert country.cities[0].name == 'London'
@@ -165,7 +165,7 @@ fn test_parse_map() {
165165
'three': 3
166166
'four': 4
167167
}
168-
out := json.decode<map[string]int>('{"one":1,"two":2,"three":3,"four":4}')!
168+
out := json.decode[map[string]int]('{"one":1,"two":2,"three":3,"four":4}')!
169169
assert out == expected
170170
}
171171

@@ -224,7 +224,7 @@ fn test_nested_type() {
224224
}
225225
out := json.encode(data)
226226
assert out == data_expected
227-
data2 := json.decode<Data>(data_expected)!
227+
data2 := json.decode[Data](data_expected)!
228228
assert data2.countries.len == data.countries.len
229229
for i in 0 .. 1 {
230230
assert data2.countries[i].name == data.countries[i].name
@@ -333,7 +333,6 @@ fn test_decode_missing_maps_field() {
333333
assert '${info.maps}' == '{}'
334334
}
335335

336-
337336
struct Foo3 {
338337
name string
339338
age int [omitempty]

0 commit comments

Comments
 (0)