diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 404f1962f8..a40332d6ac 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -452,8 +452,9 @@ pub struct StructInit { pub: pos token.Pos name_pos token.Pos - is_short bool // Foo{val1, val2} - is_short_syntax bool // foo(field1: val1, field2: val2) + no_keys bool // `Foo{val1, val2}` + is_short_syntax bool // `foo(field1: val1, field2: val2)` + is_anon bool // `x: struct{ foo: bar }` pub mut: unresolved bool pre_comments []Comment @@ -469,6 +470,12 @@ pub mut: generic_types []Type } +pub enum StructInitKind { + normal + short_syntax + anon +} + // import statement pub struct Import { pub: diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 260b1fe733..dc58640eb8 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -267,7 +267,8 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { && c.table.cur_concrete_types.len == 0 { pos := type_sym.name.last_index('.') or { -1 } first_letter := type_sym.name[pos + 1] - if !first_letter.is_capital() && type_sym.kind != .placeholder { + if !first_letter.is_capital() && type_sym.kind != .placeholder + && !type_sym.name.starts_with('main._VAnonStruct') { c.error('cannot initialize builtin type `$type_sym.name`', node.pos) } } @@ -324,7 +325,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } else { info = type_sym.info as ast.Struct } - if node.is_short { + if node.no_keys { exp_len := info.fields.len got_len := node.fields.len if exp_len != got_len && !c.pref.translated { @@ -335,7 +336,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } } mut info_fields_sorted := []ast.StructField{} - if node.is_short { + if node.no_keys { info_fields_sorted = info.fields.clone() info_fields_sorted.sort(a.i < b.i) } @@ -343,7 +344,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { for i, mut field in node.fields { mut field_info := ast.StructField{} mut field_name := '' - if node.is_short { + if node.no_keys { if i >= info.fields.len { // It doesn't make sense to check for fields that don't exist. // We should just stop here. @@ -496,7 +497,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { } */ // Check for `[required]` struct attr - if field.attrs.contains('required') && !node.is_short && !node.has_update_expr { + if field.attrs.contains('required') && !node.no_keys && !node.has_update_expr { mut found := false for init_field in node.fields { if field.name == init_field.name { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index be4c5dd4ef..4c1576f555 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -703,7 +703,7 @@ fn expr_is_single_line(expr ast.Expr) bool { } } ast.StructInit { - if !expr.is_short && (expr.fields.len > 0 || expr.pre_comments.len > 0) { + if !expr.no_keys && (expr.fields.len > 0 || expr.pre_comments.len > 0) { return false } } diff --git a/vlib/v/fmt/struct.v b/vlib/v/fmt/struct.v index 0af6423507..d73afb00da 100644 --- a/vlib/v/fmt/struct.v +++ b/vlib/v/fmt/struct.v @@ -216,6 +216,9 @@ pub fn (mut f Fmt) struct_init(node ast.StructInit) { if name == 'void' { name = '' } + if node.is_anon { + f.write('struct ') + } if node.fields.len == 0 && !node.has_update_expr { // `Foo{}` on one line if there are no fields or comments if node.pre_comments.len == 0 { @@ -226,8 +229,8 @@ pub fn (mut f Fmt) struct_init(node ast.StructInit) { f.write('}') } f.mark_import_as_used(name) - } else if node.is_short { - // `Foo{1,2,3}` (short syntax ) + } else if node.no_keys { + // `Foo{1,2,3}` (short syntax, no keys) f.write('$name{') f.mark_import_as_used(name) if node.has_update_expr { diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 4306b2527b..0923fa1986 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -22,17 +22,18 @@ fn (mut g Gen) struct_init(node ast.StructInit) { if is_amp { g.out.go_back(1) // delete the `&` already generated in `prefix_expr() } - g.write('(') - defer { - g.write(')') - } - mut is_anon := false if sym.kind == .struct_ { mut info := sym.info as ast.Struct is_anon = info.is_anon } + if !is_anon { + g.write('(') + defer { + g.write(')') + } + } if is_anon { // No name needed for anon structs, C figures it out on its own. g.writeln('{') diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index c4aaa0b2ff..7afe792a53 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -325,6 +325,11 @@ pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr { node = p.map_init() p.check(.rcbr) } + .key_struct { + // Anonymous struct + p.next() + return p.struct_init('', .anon) + } .key_fn { if p.expecting_type { // Anonymous function type diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 3897c8afb9..768ab5cd13 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -123,7 +123,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg { mut expr := ast.empty_expr() if p.tok.kind == .name && p.peek_tok.kind == .colon { // `foo(key:val, key2:val2)` - expr = p.struct_init('void_type', true) // short_syntax:true + expr = p.struct_init('void_type', .short_syntax) } else { expr = p.expr(0) } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 09d8141514..84b9298a37 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2428,12 +2428,12 @@ pub fn (mut p Parser) name_expr() ast.Expr { && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && !p.inside_match_case && (!p.inside_if || p.inside_select) && (!p.inside_for || p.inside_select) && !known_var { - return p.struct_init(p.mod + '.' + p.tok.lit, false) // short_syntax: false + return p.struct_init(p.mod + '.' + p.tok.lit, .normal) // short_syntax: false } else if p.peek_tok.kind == .lcbr && ((p.inside_if && lit0_is_capital && p.tok.lit.len > 1 && !known_var && language == .v) || (p.inside_match_case && p.tok.kind == .name && p.peek_tok.pos - p.tok.pos == p.tok.len)) { // `if a == Foo{} {...}` or `match foo { Foo{} {...} }` - return p.struct_init(p.mod + '.' + p.tok.lit, false) + return p.struct_init(p.mod + '.' + p.tok.lit, .normal) } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { // T.name if p.is_generic_name() { diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index bc3792e728..6e340f4349 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -385,12 +385,12 @@ run them via `v file.v` instead', } } -fn (mut p Parser) struct_init(typ_str string, short_syntax bool) ast.StructInit { - first_pos := (if short_syntax && p.prev_tok.kind == .lcbr { p.prev_tok } else { p.tok }).pos() +fn (mut p Parser) struct_init(typ_str string, kind ast.StructInitKind) ast.StructInit { + first_pos := (if kind == .short_syntax && p.prev_tok.kind == .lcbr { p.prev_tok } else { p.tok }).pos() p.struct_init_generic_types = []ast.Type{} - typ := if short_syntax { ast.void_type } else { p.parse_type() } + typ := if kind == .short_syntax { ast.void_type } else { p.parse_type() } p.expr_mod = '' - if !short_syntax { + if kind != .short_syntax { p.check(.lcbr) } pre_comments := p.eat_comments() @@ -459,7 +459,7 @@ fn (mut p Parser) struct_init(typ_str string, short_syntax bool) ast.StructInit } } } - if !short_syntax { + if kind != .short_syntax { p.check(.rcbr) } p.is_amp = saved_is_amp @@ -472,9 +472,10 @@ fn (mut p Parser) struct_init(typ_str string, short_syntax bool) ast.StructInit update_expr_comments: update_expr_comments has_update_expr: has_update_expr name_pos: first_pos - pos: first_pos.extend(if short_syntax { p.tok.pos() } else { p.prev_tok.pos() }) - is_short: no_keys - is_short_syntax: short_syntax + pos: first_pos.extend(if kind == .short_syntax { p.tok.pos() } else { p.prev_tok.pos() }) + no_keys: no_keys + is_short_syntax: kind == .short_syntax + is_anon: kind == .anon pre_comments: pre_comments generic_types: p.struct_init_generic_types } diff --git a/vlib/v/tests/struct_test.v b/vlib/v/tests/struct_test.v index d73abf40fa..5f5f336ea2 100644 --- a/vlib/v/tests/struct_test.v +++ b/vlib/v/tests/struct_test.v @@ -425,9 +425,25 @@ struct Book { } fn test_anon() { - empty_book := Book{} // author:struct{'sdf', 23}} + empty_book := Book{} assert empty_book.author.age == 0 assert empty_book.author.name == '' - println(empty_book.author.age) + + book := Book{ + author: struct {'Peter Brown', 23} + } + assert book.author.name == 'Peter Brown' + assert book.author.age == 23 + println(book.author.name) + + book2 := Book{ + author: struct { + name: 'Samantha Black' + age: 24 + } + } + assert book2.author.name == 'Samantha Black' + assert book2.author.age == 24 + println(book2.author.name) }