diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 15937aaaac..f2e40e317c 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -161,6 +161,16 @@ pub fn (mut p Parser) call_args() []ast.CallArg { return args } +struct ReceiverParsingInfo { +mut: + name string + pos token.Position + typ table.Type + type_pos token.Position + is_mut bool + language table.Language +} + fn (mut p Parser) fn_decl() ast.FnDecl { p.top_level_statement_start() start_pos := p.tok.position() @@ -188,73 +198,21 @@ fn (mut p Parser) fn_decl() ast.FnDecl { p.check_for_impure_v(language, p.tok.position()) } // Receiver? - mut rec_name := '' + mut rec := ReceiverParsingInfo{ + typ: table.void_type + language: language + } mut is_method := false - mut receiver_pos := token.Position{} - mut rec_type_pos := token.Position{} - mut rec_type := table.void_type - mut rec_mut := false mut params := []table.Param{} if p.tok.kind == .lpar { - lpar_pos := p.tok.position() - p.next() // ( is_method = true - is_shared := p.tok.kind == .key_shared - is_atomic := p.tok.kind == .key_atomic - rec_mut = p.tok.kind == .key_mut || is_shared || is_atomic - if rec_mut { - p.next() // `mut` - } - rec_start_pos := p.tok.position() - rec_name = p.check_name() - if !rec_mut { - rec_mut = p.tok.kind == .key_mut - if rec_mut { - p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`', lpar_pos.extend(p.peek_tok2.position())) - } - } - if p.tok.kind == .key_shared { - p.error_with_pos('use `(shared f Foo)` instead of `(f shared Foo)`', lpar_pos.extend(p.peek_tok2.position())) - } - receiver_pos = rec_start_pos.extend(p.tok.position()) - is_amp := p.tok.kind == .amp - if p.tok.kind == .name && p.tok.lit == 'JS' { - language = table.Language.js - } - // if rec_mut { - // p.check(.key_mut) - // } - // TODO: talk to alex, should mut be parsed with the type like this? - // or should it be a property of the arg, like this ptr/mut becomes indistinguishable - rec_type_pos = p.tok.position() - rec_type = p.parse_type_with_mut(rec_mut) - if rec_type.idx() == 0 { - // error is set in parse_type - return ast.FnDecl{ - scope: 0 - } - } - rec_type_pos = rec_type_pos.extend(p.prev_tok.position()) - if is_amp && rec_mut { - p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`', - lpar_pos.extend(p.tok.position())) - return ast.FnDecl{ - scope: 0 - } - } - if is_shared { - rec_type = rec_type.set_flag(.shared_f) - } - if is_atomic { - rec_type = rec_type.set_flag(.atomic_f) - } - params << table.Param{ - pos: rec_start_pos - name: rec_name - is_mut: rec_mut - typ: rec_type - } - p.check(.rpar) + p.fn_receiver(mut params, mut rec) or { return ast.FnDecl{ + scope: 0 + } } + + // rec.language was initialized with language variable. + // So language is changed only if rec.language has been changed. + language = rec.language } mut name := '' if p.tok.kind == .name { @@ -268,7 +226,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { scope: 0 } } - type_sym := p.table.get_type_symbol(rec_type) + type_sym := p.table.get_type_symbol(rec.typ) // interfaces are handled in the checker, methods can not be defined on them this way if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) { p.error_with_pos('duplicate method `$name`', pos) @@ -285,7 +243,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } } else if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && p.peek_tok.kind == .lpar { name = p.tok.kind.str() // op_to_fn_name() - if rec_type == table.void_type { + if rec.typ == table.void_type { p.error_with_pos('cannot use operator overloading with normal functions', p.tok.position()) } @@ -337,20 +295,20 @@ fn (mut p Parser) fn_decl() ast.FnDecl { no_body := p.tok.kind != .lcbr // Register if is_method { - mut type_sym := p.table.get_type_symbol(rec_type) + mut type_sym := p.table.get_type_symbol(rec.typ) // Do not allow to modify / add methods to types from other modules // arrays/maps dont belong to a module only their element types do // we could also check if kind is .array, .array_fixed, .map instead of mod.len mut is_non_local := type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v // check maps & arrays, must be defined in same module as the elem type if !is_non_local && type_sym.kind in [.array, .map] { - elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec_type)) + elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec.typ)) is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod && elem_type_sym.language == .v } if is_non_local { p.error_with_pos('cannot define new methods on non-local type $type_sym.name', - rec_type_pos) + rec.type_pos) return ast.FnDecl{ scope: 0 } @@ -427,15 +385,15 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_pub: is_pub is_variadic: is_variadic receiver: ast.Field{ - name: rec_name - typ: rec_type + name: rec.name + typ: rec.typ } generic_params: generic_params - receiver_pos: receiver_pos + receiver_pos: rec.pos is_method: is_method - method_type_pos: rec_type_pos + method_type_pos: rec.type_pos method_idx: type_sym_method_idx - rec_mut: rec_mut + rec_mut: rec.is_mut language: language no_body: no_body pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr) @@ -451,6 +409,65 @@ fn (mut p Parser) fn_decl() ast.FnDecl { return fn_decl } +fn (mut p Parser) fn_receiver(mut params []table.Param, mut rec ReceiverParsingInfo) ? { + lpar_pos := p.tok.position() + p.next() // ( + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + rec.is_mut = p.tok.kind == .key_mut || is_shared || is_atomic + if rec.is_mut { + p.next() // `mut` + } + rec_start_pos := p.tok.position() + rec.name = p.check_name() + if !rec.is_mut { + rec.is_mut = p.tok.kind == .key_mut + if rec.is_mut { + p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`', lpar_pos.extend(p.peek_tok2.position())) + } + } + if p.tok.kind == .key_shared { + p.error_with_pos('use `(shared f Foo)` instead of `(f shared Foo)`', lpar_pos.extend(p.peek_tok2.position())) + } + rec.pos = rec_start_pos.extend(p.tok.position()) + is_amp := p.tok.kind == .amp + if p.tok.kind == .name && p.tok.lit == 'JS' { + rec.language = table.Language.js + } + // if rec.is_mut { + // p.check(.key_mut) + // } + // TODO: talk to alex, should mut be parsed with the type like this? + // or should it be a property of the arg, like this ptr/mut becomes indistinguishable + rec.type_pos = p.tok.position() + rec.typ = p.parse_type_with_mut(rec.is_mut) + if rec.typ.idx() == 0 { + // error is set in parse_type + return none + } + rec.type_pos = rec.type_pos.extend(p.prev_tok.position()) + if is_amp && rec.is_mut { + p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`', + lpar_pos.extend(p.tok.position())) + return none + } + if is_shared { + rec.typ = rec.typ.set_flag(.shared_f) + } + if is_atomic { + rec.typ = rec.typ.set_flag(.atomic_f) + } + params << table.Param{ + pos: rec_start_pos + name: rec.name + is_mut: rec.is_mut + typ: rec.typ + } + p.check(.rpar) + + return +} + fn (mut p Parser) parse_generic_params() []ast.GenericParam { mut param_names := []string{} if p.tok.kind != .lt {