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:
parent
83ee2827d4
commit
89c56fb5ee
@ -6,6 +6,24 @@ module ast
|
|||||||
import v.util
|
import v.util
|
||||||
import strings
|
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 {
|
pub fn (node &FnDecl) modname() string {
|
||||||
if node.mod != '' {
|
if node.mod != '' {
|
||||||
return node.mod
|
return node.mod
|
||||||
@ -373,18 +391,18 @@ pub fn (x Expr) str() string {
|
|||||||
return '${x.left.str()}.${x.name}(${sargs})${propagate_suffix}'
|
return '${x.left.str()}.${x.name}(${sargs})${propagate_suffix}'
|
||||||
}
|
}
|
||||||
if x.name.starts_with('${x.mod}.') {
|
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 == '' {
|
if x.mod == '' && x.name == '' {
|
||||||
return x.left.str() + '(${sargs})${propagate_suffix}'
|
return x.left.str() + '(${sargs})${propagate_suffix}'
|
||||||
}
|
}
|
||||||
if x.name.contains('.') {
|
if x.name.contains('.') {
|
||||||
return '${x.name}(${sargs})${propagate_suffix}'
|
return '${x.get_name()}(${sargs})${propagate_suffix}'
|
||||||
}
|
}
|
||||||
if x.name.contains('__static__') {
|
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 {
|
CharLiteral {
|
||||||
return '`${x.val}`'
|
return '`${x.val}`'
|
||||||
|
@ -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.
|
// If it's a void type, it's an unknown variable, already had an error earlier.
|
||||||
return
|
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)
|
node.pos)
|
||||||
} else if right_first is ast.ParExpr {
|
} else if right_first is ast.ParExpr {
|
||||||
mut right_next := right_first
|
mut right_next := right_first
|
||||||
for {
|
for {
|
||||||
if mut right_next.expr is ast.CallExpr {
|
if mut right_next.expr is ast.CallExpr {
|
||||||
if right_next.expr.return_type == ast.void_type {
|
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)
|
node.pos)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -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 {
|
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 {
|
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)
|
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
|
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
|
return ast.void_type
|
||||||
}
|
}
|
||||||
node.is_file_translated = func.is_file_translated
|
node.is_file_translated = func.is_file_translated
|
||||||
|
12
vlib/v/checker/tests/func_with_static_keyword_err.out
Normal file
12
vlib/v/checker/tests/func_with_static_keyword_err.out
Normal 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 | }
|
7
vlib/v/checker/tests/func_with_static_keyword_err.vv
Normal file
7
vlib/v/checker/tests/func_with_static_keyword_err.vv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fn a__static__b() (int,int) {
|
||||||
|
return 1,2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := a__static__b()
|
||||||
|
}
|
7
vlib/v/checker/tests/static_method_multi_return_err.out
Normal file
7
vlib/v/checker/tests/static_method_multi_return_err.out
Normal 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 | }
|
12
vlib/v/checker/tests/static_method_multi_return_err.vv
Normal file
12
vlib/v/checker/tests/static_method_multi_return_err.vv
Normal 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)
|
||||||
|
}
|
13
vlib/v/checker/tests/static_method_not_found_err.out
Normal file
13
vlib/v/checker/tests/static_method_not_found_err.out
Normal 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 | }
|
12
vlib/v/checker/tests/static_method_not_found_err.vv
Normal file
12
vlib/v/checker/tests/static_method_not_found_err.vv
Normal 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)
|
||||||
|
}
|
@ -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
|
mut is_static_type_method := language == .v && name[0].is_capital() && p.tok.kind == .dot
|
||||||
if is_static_type_method {
|
if is_static_type_method {
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
name = name.to_lower() + '__static__' + p.check_name()
|
name = name + '__static__' + p.check_name()
|
||||||
}
|
}
|
||||||
mut fn_name := if language == .c {
|
mut fn_name := if language == .c {
|
||||||
'C.${name}'
|
'C.${name}'
|
||||||
@ -294,6 +294,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||||||
mut type_sym := p.table.sym(rec.typ)
|
mut type_sym := p.table.sym(rec.typ)
|
||||||
name_pos := p.tok.pos()
|
name_pos := p.tok.pos()
|
||||||
if p.tok.kind == .name {
|
if p.tok.kind == .name {
|
||||||
|
mut check_name := ''
|
||||||
// TODO high order fn
|
// TODO high order fn
|
||||||
is_static_type_method = p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
|
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() {}`
|
&& 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"
|
type_name := p.tok.lit // "Foo"
|
||||||
rec.typ = p.parse_type()
|
rec.typ = p.parse_type()
|
||||||
p.check(.dot)
|
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 {
|
} 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)
|
if language == .v && !p.pref.translated && !p.is_translated
|
||||||
&& !p.builtin_mod {
|
&& util.contains_capital(check_name) && !p.builtin_mod {
|
||||||
p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
|
p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead',
|
||||||
name_pos)
|
name_pos)
|
||||||
return ast.FnDecl{
|
return ast.FnDecl{
|
||||||
|
@ -2630,11 +2630,12 @@ fn (mut p Parser) name_expr() ast.Expr {
|
|||||||
// type cast. TODO: finish
|
// type cast. TODO: finish
|
||||||
// if name in ast.builtin_type_names_to_idx {
|
// 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
|
// 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
|
if (is_option || p.peek_tok.kind in [.lsbr, .lt, .lpar]) && (is_mod_cast
|
||||||
|| is_generic_cast || (language == .v && name.len > 0 && (name[0].is_capital()
|
|| 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
|
|| (!known_var && (name in p.table.type_idxs
|
||||||
|| name_w_mod 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.`
|
// MainLetter(x) is *always* a cast, as long as it is not `C.`
|
||||||
// TODO handle C.stat()
|
// TODO handle C.stat()
|
||||||
start_pos := p.tok.pos()
|
start_pos := p.tok.pos()
|
||||||
@ -2673,7 +2674,6 @@ fn (mut p Parser) name_expr() ast.Expr {
|
|||||||
p.expr_mod = ''
|
p.expr_mod = ''
|
||||||
return node
|
return node
|
||||||
} else {
|
} else {
|
||||||
// fn call
|
|
||||||
// fn_call
|
// fn_call
|
||||||
if is_option {
|
if is_option {
|
||||||
p.unexpected_with_pos(p.prev_tok.pos(),
|
p.unexpected_with_pos(p.prev_tok.pos(),
|
||||||
|
@ -12,7 +12,7 @@ vlib/v/parser/tests/method_call_receiver_err.vv:8:7: warning: unused variable: `
|
|||||||
| ~~~~~~
|
| ~~~~~~
|
||||||
9 | println(S1.method_hello('yo'))
|
9 | println(S1.method_hello('yo'))
|
||||||
10 | }
|
10 | }
|
||||||
vlib/v/parser/tests/method_call_receiver_err.vv:9:11: error: unknown function: s1__static__method_hello
|
vlib/v/parser/tests/method_call_receiver_err.vv:9:11: error: unknown function: S1.method_hello
|
||||||
7 |
|
7 |
|
||||||
8 | $for method in S1.methods {
|
8 | $for method in S1.methods {
|
||||||
9 | println(S1.method_hello('yo'))
|
9 | println(S1.method_hello('yo'))
|
||||||
|
12
vlib/v/tests/generic_static_method_test.v
Normal file
12
vlib/v/tests/generic_static_method_test.v
Normal 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'
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user