1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

parser, checker: fix static method naming and generic call (#18694)

This commit is contained in:
Felipe Pena 2023-06-28 01:07:07 -03:00 committed by GitHub
parent 83ee2827d4
commit 89c56fb5ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 118 additions and 22 deletions

View File

@ -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}`'

View File

@ -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

View File

@ -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

View File

@ -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 | }

View File

@ -0,0 +1,7 @@
fn a__static__b() (int,int) {
return 1,2
}
fn main() {
a := a__static__b()
}

View File

@ -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 | }

View File

@ -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)
}

View File

@ -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 | }

View File

@ -0,0 +1,12 @@
struct TestStruct {
name string
}
fn (f TestStruct) static_method() string {
return 'hello'
}
fn main() {
val := TestStruct.static_method()
println(val)
}

View File

@ -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{

View File

@ -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(),

View File

@ -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'))
| ~~~~~~~~~~~~~~~~~~~~~

View File

@ -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'
}