diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 49ed3605a3..3d981e847a 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -6,6 +6,24 @@ module ast import v.util import strings +// get_name returns the real name for the function declaration +pub fn (f &FnDecl) get_name() string { + if f.is_static_type_method { + return f.name.all_after_last('__static__') + } else { + return f.name + } +} + +// get_name returns the real name for the function calling +pub fn (f &CallExpr) get_name() string { + if f.name != '' && f.name.all_after_last('.')[0].is_capital() && f.name.contains('__static__') { + return f.name.replace('__static__', '.') + } else { + return f.name + } +} + pub fn (node &FnDecl) modname() string { if node.mod != '' { return node.mod @@ -373,18 +391,18 @@ pub fn (x Expr) str() string { return '${x.left.str()}.${x.name}(${sargs})${propagate_suffix}' } if x.name.starts_with('${x.mod}.') { - return util.strip_main_name('${x.name}(${sargs})${propagate_suffix}') + return util.strip_main_name('${x.get_name()}(${sargs})${propagate_suffix}') } if x.mod == '' && x.name == '' { return x.left.str() + '(${sargs})${propagate_suffix}' } if x.name.contains('.') { - return '${x.name}(${sargs})${propagate_suffix}' + return '${x.get_name()}(${sargs})${propagate_suffix}' } if x.name.contains('__static__') { - return '${x.mod}.${x.name}(${sargs})${propagate_suffix}' + return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}' } - return '${x.mod}.${x.name}(${sargs})${propagate_suffix}' + return '${x.mod}.${x.get_name()}(${sargs})${propagate_suffix}' } CharLiteral { return '`${x.val}`' diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index c658a3a567..cf89598051 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -87,14 +87,14 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { // If it's a void type, it's an unknown variable, already had an error earlier. return } - c.error('assignment mismatch: ${node.left.len} variable(s) but `${right_first.name}()` returns ${right_len} value(s)', + c.error('assignment mismatch: ${node.left.len} variable(s) but `${right_first.get_name()}()` returns ${right_len} value(s)', node.pos) } else if right_first is ast.ParExpr { mut right_next := right_first for { if mut right_next.expr is ast.CallExpr { if right_next.expr.return_type == ast.void_type { - c.error('assignment mismatch: expected ${node.left.len} value(s) but `${right_next.expr.name}()` returns ${right_len} value(s)', + c.error('assignment mismatch: expected ${node.left.len} value(s) but `${right_next.expr.get_name()}()` returns ${right_len} value(s)', node.pos) } break diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index e7846d1b98..bae8639fa3 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -87,7 +87,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } } if node.language == .v && !c.is_builtin_mod && !node.is_anon { - c.check_valid_snake_case(node.name, 'function name', node.pos) + c.check_valid_snake_case(node.get_name(), 'function name', node.pos) if !node.is_method && node.mod == 'main' && node.short_name in c.table.builtin_pub_fns { c.error('cannot redefine builtin public function `${node.short_name}`', node.pos) } @@ -944,7 +944,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. return ast.void_type } } - c.error('unknown function: ${fn_name}', node.pos) + c.error('unknown function: ${node.get_name()}', node.pos) return ast.void_type } node.is_file_translated = func.is_file_translated diff --git a/vlib/v/checker/tests/func_with_static_keyword_err.out b/vlib/v/checker/tests/func_with_static_keyword_err.out new file mode 100644 index 0000000000..ab97f52de0 --- /dev/null +++ b/vlib/v/checker/tests/func_with_static_keyword_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/func_with_static_keyword_err.vv:6:2: warning: unused variable: `a` + 4 | + 5 | fn main() { + 6 | a := a__static__b() + | ^ + 7 | } +vlib/v/checker/tests/func_with_static_keyword_err.vv:6:4: error: assignment mismatch: 1 variable(s) but `a__static__b()` returns 2 value(s) + 4 | + 5 | fn main() { + 6 | a := a__static__b() + | ~~ + 7 | } diff --git a/vlib/v/checker/tests/func_with_static_keyword_err.vv b/vlib/v/checker/tests/func_with_static_keyword_err.vv new file mode 100644 index 0000000000..f2ca685155 --- /dev/null +++ b/vlib/v/checker/tests/func_with_static_keyword_err.vv @@ -0,0 +1,7 @@ +fn a__static__b() (int,int) { + return 1,2 +} + +fn main() { + a := a__static__b() +} \ No newline at end of file diff --git a/vlib/v/checker/tests/static_method_multi_return_err.out b/vlib/v/checker/tests/static_method_multi_return_err.out new file mode 100644 index 0000000000..64f8c9ac25 --- /dev/null +++ b/vlib/v/checker/tests/static_method_multi_return_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/static_method_multi_return_err.vv:10:6: error: assignment mismatch: 1 variable(s) but `SomeStruct.static_method()` returns 2 value(s) + 8 | + 9 | fn main() { + 10 | val := SomeStruct.static_method() + | ~~ + 11 | println(val) + 12 | } diff --git a/vlib/v/checker/tests/static_method_multi_return_err.vv b/vlib/v/checker/tests/static_method_multi_return_err.vv new file mode 100644 index 0000000000..7b3935822a --- /dev/null +++ b/vlib/v/checker/tests/static_method_multi_return_err.vv @@ -0,0 +1,12 @@ +struct SomeStruct { + name string +} + +fn SomeStruct.static_method() (string, int) { + return 'hello', 100 +} + +fn main() { + val := SomeStruct.static_method() + println(val) +} \ No newline at end of file diff --git a/vlib/v/checker/tests/static_method_not_found_err.out b/vlib/v/checker/tests/static_method_not_found_err.out new file mode 100644 index 0000000000..0cd3b82822 --- /dev/null +++ b/vlib/v/checker/tests/static_method_not_found_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/static_method_not_found_err.vv:10:9: error: unknown function: TestStruct.static_method + 8 | + 9 | fn main() { + 10 | val := TestStruct.static_method() + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | println(val) + 12 | } +vlib/v/checker/tests/static_method_not_found_err.vv:11:2: error: `println` can not print void expressions + 9 | fn main() { + 10 | val := TestStruct.static_method() + 11 | println(val) + | ~~~~~~~~~~~~ + 12 | } diff --git a/vlib/v/checker/tests/static_method_not_found_err.vv b/vlib/v/checker/tests/static_method_not_found_err.vv new file mode 100644 index 0000000000..1408607544 --- /dev/null +++ b/vlib/v/checker/tests/static_method_not_found_err.vv @@ -0,0 +1,12 @@ +struct TestStruct { + name string +} + +fn (f TestStruct) static_method() string { + return 'hello' +} + +fn main() { + val := TestStruct.static_method() + println(val) +} \ No newline at end of file diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index b71c26d699..18c6c995fc 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -14,7 +14,7 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { mut is_static_type_method := language == .v && name[0].is_capital() && p.tok.kind == .dot if is_static_type_method { p.check(.dot) - name = name.to_lower() + '__static__' + p.check_name() + name = name + '__static__' + p.check_name() } mut fn_name := if language == .c { 'C.${name}' @@ -294,6 +294,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { mut type_sym := p.table.sym(rec.typ) name_pos := p.tok.pos() if p.tok.kind == .name { + mut check_name := '' // TODO high order fn is_static_type_method = p.tok.lit.len > 0 && p.tok.lit[0].is_capital() && p.peek_tok.kind == .dot && language == .v // `fn Foo.bar() {}` @@ -301,12 +302,14 @@ fn (mut p Parser) fn_decl() ast.FnDecl { type_name := p.tok.lit // "Foo" rec.typ = p.parse_type() p.check(.dot) - name = type_name.to_lower() + '__static__' + p.check_name() // "foo__bar" + check_name = p.check_name() + name = type_name + '__static__' + check_name // "foo__bar" } else { - name = if language == .js { p.check_js_name() } else { p.check_name() } + check_name = if language == .js { p.check_js_name() } else { p.check_name() } + name = check_name } - if language == .v && !p.pref.translated && !p.is_translated && util.contains_capital(name) - && !p.builtin_mod { + if language == .v && !p.pref.translated && !p.is_translated + && util.contains_capital(check_name) && !p.builtin_mod { p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead', name_pos) return ast.FnDecl{ diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 3f8d811364..cb72dc65c0 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2630,11 +2630,12 @@ fn (mut p Parser) name_expr() ast.Expr { // type cast. TODO: finish // if name in ast.builtin_type_names_to_idx { // handle the easy cases first, then check for an already known V typename, not shadowed by a local variable - if is_mod_cast || is_c_pointer_cast || is_c_type_cast || is_js_cast - || is_generic_cast || (language == .v && name.len > 0 && (name[0].is_capital() + if (is_option || p.peek_tok.kind in [.lsbr, .lt, .lpar]) && (is_mod_cast + || is_c_pointer_cast || is_c_type_cast || is_js_cast || is_generic_cast + || (language == .v && name.len > 0 && (name[0].is_capital() || (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs)) - || name.all_after_last('.')[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() @@ -2673,7 +2674,6 @@ fn (mut p Parser) name_expr() ast.Expr { p.expr_mod = '' return node } else { - // fn call // fn_call if is_option { p.unexpected_with_pos(p.prev_tok.pos(), diff --git a/vlib/v/parser/tests/method_call_receiver_err.out b/vlib/v/parser/tests/method_call_receiver_err.out index 13503ca7b2..0351a23083 100644 --- a/vlib/v/parser/tests/method_call_receiver_err.out +++ b/vlib/v/parser/tests/method_call_receiver_err.out @@ -1,19 +1,19 @@ vlib/v/parser/tests/method_call_receiver_err.vv:6:2: warning: unused variable: `s1` - 4 | + 4 | 5 | fn main() { 6 | s1 := S1{} | ~~ - 7 | + 7 | 8 | $for method in S1.methods { vlib/v/parser/tests/method_call_receiver_err.vv:8:7: warning: unused variable: `method` 6 | s1 := S1{} - 7 | + 7 | 8 | $for method in S1.methods { | ~~~~~~ 9 | println(S1.method_hello('yo')) 10 | } -vlib/v/parser/tests/method_call_receiver_err.vv:9:11: error: unknown function: s1__static__method_hello - 7 | +vlib/v/parser/tests/method_call_receiver_err.vv:9:11: error: unknown function: S1.method_hello + 7 | 8 | $for method in S1.methods { 9 | println(S1.method_hello('yo')) | ~~~~~~~~~~~~~~~~~~~~~ diff --git a/vlib/v/tests/generic_static_method_test.v b/vlib/v/tests/generic_static_method_test.v new file mode 100644 index 0000000000..722e0d72f9 --- /dev/null +++ b/vlib/v/tests/generic_static_method_test.v @@ -0,0 +1,12 @@ +struct SomeStruct { + name string +} + +fn SomeStruct.static_method[T]() string { + return 'hello' +} + +fn test_main() { + val := SomeStruct.static_method[string]() + assert val == 'hello' +}