diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v index de4ea97158..7f999916e4 100644 --- a/vlib/v/parser/containers.v +++ b/vlib/v/parser/containers.v @@ -6,12 +6,8 @@ module parser import v.ast import v.token -fn (mut p Parser) array_init() ast.ArrayInit { +fn (mut p Parser) array_init(is_option bool) ast.ArrayInit { first_pos := p.tok.pos() - is_option := p.tok.kind == .question - if is_option { - p.next() - } mut last_pos := p.tok.pos() p.check(.lsbr) // p.warn('array_init() exp=$p.expected_type') diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 9b7b33ed87..aaf812ca58 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -195,7 +195,7 @@ pub fn (mut p Parser) check_expr(precedence int) !ast.Expr { pos: pos } } else { - node = p.array_init() + node = p.array_init(false) } } .key_none { @@ -436,7 +436,7 @@ pub fn (mut p Parser) check_expr(precedence int) !ast.Expr { if p.tok.kind == .key_struct && p.peek_tok.kind == .lcbr { // Anonymous struct p.next() - return p.struct_init('', .anon) + return p.struct_init('', .anon, false) } if p.tok.kind != .eof && !(p.tok.kind == .rsbr && p.inside_asm) { // eof should be handled where it happens diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 27b6a084cf..e6f7cfb227 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -113,7 +113,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', .short_syntax) + expr = p.struct_init('void_type', .short_syntax, false) } else { expr = p.expr(0) } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index dd9835d5f2..96523b10a4 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2393,14 +2393,19 @@ pub fn (mut p Parser) name_expr() ast.Expr { language = ast.Language.wasm p.check_for_impure_v(language, p.tok.pos()) } + is_option := p.tok.kind == .question + if is_option { + if p.peek_tok.kind in [.name, .lsbr] { + p.check(.question) + } + } mut mod := '' // p.warn('resetting') p.expr_mod = '' // `map[string]int` initialization - if (p.tok.lit == 'map' && p.peek_tok.kind == .lsbr) - || (p.tok.kind == .question && p.peek_tok.lit == 'map') { + if p.tok.lit == 'map' && p.peek_tok.kind == .lsbr { mut pos := p.tok.pos() - map_type := p.parse_map_type() + mut map_type := p.parse_map_type() if p.tok.kind == .lcbr { p.next() if p.tok.kind == .rcbr { @@ -2415,6 +2420,9 @@ pub fn (mut p Parser) name_expr() ast.Expr { p.error('`}` expected; explicit `map` initialization does not support parameters') } } + if is_option { + map_type = map_type.set_flag(.option) + } return ast.MapInit{ typ: map_type pos: pos @@ -2519,17 +2527,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { p.check(.dot) p.expr_mod = mod } - is_option := p.tok.kind == .question lit0_is_capital := if p.tok.kind != .eof && p.tok.lit.len > 0 { - if is_option { - if p.peek_tok.kind != .eof && p.peek_tok.lit.len > 0 { - p.peek_tok.lit[0].is_capital() - } else { - false - } - } else { - p.tok.lit[0].is_capital() - } + p.tok.lit[0].is_capital() } else { false } @@ -2550,18 +2549,14 @@ pub fn (mut p Parser) name_expr() ast.Expr { } } } else if p.peek_tok.kind == .lpar || is_generic_call || is_generic_cast - || (is_option && p.peek_token(2).kind == .lpar) || (is_option && ((p.peek_tok.kind == .lsbr - && p.peek_token(2).kind == .rsbr && p.peek_token(3).kind == .name - && p.peek_token(4).kind == .lpar) || (p.peek_tok.kind == .lsbr - && p.peek_token(2).kind == .number && p.peek_token(3).kind == .rsbr - && p.peek_token(4).kind == .name && p.peek_token(5).kind == .lpar))) { - is_array := p.peek_tok.kind == .lsbr - is_fixed_array := is_array && p.peek_token(2).kind == .number - // foo(), foo() or type() cast - mut name := if is_option { - if is_array { p.peek_token(if is_fixed_array { 4 } else { 3 }).lit - } else { p.peek_tok.lit - } + || (p.tok.kind == .lsbr && p.peek_tok.kind == .rsbr && p.peek_token(3).kind == .lpar) + || (p.tok.kind == .lsbr && p.peek_tok.kind == .number && p.peek_token(2).kind == .rsbr + && p.peek_token(4).kind == .lpar) { + // ?[]foo(), ?[1]foo, foo(), foo() or type() cast + is_array := p.tok.kind == .lsbr + is_fixed_array := is_array && p.peek_tok.kind == .number + mut name := if is_array { + p.peek_token(if is_fixed_array { 3 } else { 2 }).lit } else { p.tok.lit } @@ -2573,8 +2568,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { // if name in ast.builtin_type_names_to_idx { if (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs) && name !in ['C.statvfs', 'C.stat', 'C.sigaction']) || is_mod_cast - || is_generic_cast - || (language == .v && name.len > 0 && name[0].is_capital()) { + || is_generic_cast || (language == .v && name.len > 0 && (name[0].is_capital() + || name.all_after_last('.')[0].is_capital())) { // MainLetter(x) is *always* a cast, as long as it is not `C.` // TODO handle C.stat() start_pos := p.tok.pos() @@ -2599,6 +2594,9 @@ pub fn (mut p Parser) name_expr() ast.Expr { } end_pos := p.tok.pos() p.check(.rpar) + if is_option { + to_typ = to_typ.set_flag(.option) + } node = ast.CastExpr{ typ: to_typ typname: p.table.sym(to_typ).name @@ -2630,17 +2628,16 @@ pub fn (mut p Parser) name_expr() ast.Expr { } } } - } else if (((!is_option && p.peek_tok.kind == .lcbr) - || (is_option && p.peek_token(2).kind == .lcbr)) || is_generic_struct_init) + } else if (p.peek_tok.kind == .lcbr || is_generic_struct_init) && (!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, .normal) // short_syntax: false + return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option) // 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, .normal) + return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option) } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { // T.name if p.is_generic_name() { @@ -2696,7 +2693,10 @@ pub fn (mut p Parser) name_expr() ast.Expr { || p.table.find_type_idx(p.mod + '.' + p.tok.lit) > 0 || p.inside_comptime_if) { type_pos := p.tok.pos() - typ := p.parse_type() + mut typ := p.parse_type() + if is_option { + typ = typ.set_flag(.option) + } return ast.TypeNode{ typ: typ pos: type_pos @@ -2719,8 +2719,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { } p.expr_mod = '' return node - } else if p.tok.kind == .question && p.peek_tok.kind == .lsbr { - return p.array_init() + } else if is_option && p.tok.kind == .lsbr { + return p.array_init(is_option) } ident := p.parse_ident(language) node = ident diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index acf0cbbb00..b95d7aabf6 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -399,10 +399,13 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { } } -fn (mut p Parser) struct_init(typ_str string, kind ast.StructInitKind) ast.StructInit { +fn (mut p Parser) struct_init(typ_str string, kind ast.StructInitKind, is_option bool) 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 kind == .short_syntax { ast.void_type } else { p.parse_type() } + mut typ := if kind == .short_syntax { ast.void_type } else { p.parse_type() } + if is_option { + typ = typ.set_flag(.option) + } p.expr_mod = '' if kind != .short_syntax { p.check(.lcbr) diff --git a/vlib/v/tests/option_import_struct_test.v b/vlib/v/tests/option_import_struct_test.v new file mode 100644 index 0000000000..42a96c089a --- /dev/null +++ b/vlib/v/tests/option_import_struct_test.v @@ -0,0 +1,17 @@ +module main + +import another_module as aaa + +fn test_import_opt_struct_from_another_module() { + x := ?aaa.SomeStruct{} + assert dump(x == none) == true +} + +fn test(a ?aaa.SomeStruct) ?aaa.SomeStruct { + return none +} + +fn test_fn_decl_with_struct_from_another_module() { + x := test(?aaa.SomeStruct(none)) + assert dump(x == none) == true +}