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

checker: fix compile-time call with string identifier expression (#8415)

This commit is contained in:
Nick Treleaven 2021-01-30 14:24:16 +00:00 committed by GitHub
parent 058f3ba013
commit 43c3a3b080
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 123 additions and 25 deletions

View File

@ -1130,6 +1130,8 @@ pub struct ComptimeCall {
pub:
has_parens bool // if $() is used, for vfmt
method_name string
method_pos token.Position
scope &Scope
left Expr
is_vweb bool
vweb_tmpl File
@ -1137,7 +1139,8 @@ pub:
is_embed bool
embed_file EmbeddedFile
pub mut:
sym table.TypeSymbol
sym table.TypeSymbol
result_type table.Type
}
pub struct None {

View File

@ -3677,14 +3677,39 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
c.nr_errors += c2.nr_errors
}
if node.method_name == 'html' {
return c.table.find_type_idx('vweb.Result')
rtyp := c.table.find_type_idx('vweb.Result')
node.result_type = rtyp
return rtyp
}
mut var := c.fn_scope.objects[node.method_name]
if mut var is ast.Var {
var.is_used = true
c.fn_scope.objects[node.method_name] = var
if node.is_vweb || node.method_name == 'method' {
return table.string_type
}
return table.string_type
// s.$my_str()
v := node.scope.find_var(node.method_name) or {
c.error('unknown identifier `$node.method_name`', node.method_pos)
return table.void_type
}
if v.typ != table.string_type {
s := c.expected_msg(v.typ, table.string_type)
c.error('invalid string method call: $s', node.method_pos)
return table.void_type
}
// note: we should use a compile-time evaluation function rather than handle here
// mut variables will not work after init
mut method_name := ''
if v.expr is ast.StringLiteral {
method_name = v.expr.val
} else {
c.add_error_detail(v.expr.type_name())
c.error('todo: not a string literal', node.method_pos)
}
f := node.sym.find_method(method_name) or {
c.error('could not find method `$method_name`', node.method_pos)
return table.void_type
}
println(f.name + ' ' + c.table.type_to_str(f.return_type))
node.result_type = f.return_type
return f.return_type
}
fn (mut c Checker) at_expr(mut node ast.AtExpr) table.Type {

View File

@ -1 +1,29 @@
print void
vlib/v/checker/tests/comptime_call_no_unused_var.vv:11:8: error: unknown identifier `w`
9 | abc := 'print'
10 | test.$abc() // OK
11 | test.$w()
| ^
12 | v := 4
13 | test.$v()
vlib/v/checker/tests/comptime_call_no_unused_var.vv:13:8: error: invalid string method call: expected `string`, not `int`
11 | test.$w()
12 | v := 4
13 | test.$v()
| ^
14 | s := 'x' + 'y'
15 | test.$s()
vlib/v/checker/tests/comptime_call_no_unused_var.vv:15:8: error: todo: not a string literal
13 | test.$v()
14 | s := 'x' + 'y'
15 | test.$s()
| ^
16 | s2 := 'x'
17 | test.$s2()
details: v.ast.InfixExpr
vlib/v/checker/tests/comptime_call_no_unused_var.vv:17:8: error: could not find method `x`
15 | test.$s()
16 | s2 := 'x'
17 | test.$s2()
| ~~
18 | }

View File

@ -7,5 +7,12 @@ fn (test Test) print() {
fn main() {
test := Test{}
abc := 'print'
test.$abc()
}
test.$abc() // OK
test.$w()
v := 4
test.$v()
s := 'x' + 'y'
test.$s()
s2 := 'x'
test.$s2()
}

View File

@ -57,9 +57,7 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
}
return
}
g.writeln('// $' + 'method call. sym="$node.sym.name"')
mut j := 0
result_type := g.table.find_type_idx('vweb.Result') // TODO not just vweb
g.writeln('// \$method call. sym="$node.sym.name"')
if node.method_name == 'method' {
// `app.$method()`
m := node.sym.find_method(g.comp_for_method) or { return }
@ -95,9 +93,10 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
g.write(' ); // vweb action call with args')
return
}
mut j := 0
for method in node.sym.methods {
// if method.return_type != table.void_type {
if method.return_type != result_type {
if method.return_type != node.result_type {
continue
}
if method.params.len != 1 {
@ -108,10 +107,12 @@ fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
// p.error('`$p.expr_var.name` needs to be a reference')
// }
amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
if j > 0 {
g.write(' else ')
if node.is_vweb {
if j > 0 {
g.write(' else ')
}
g.write('if (string_eq($node.method_name, _SLIT("$method.name"))) ')
}
g.write('if (string_eq($node.method_name, _SLIT("$method.name"))) ')
g.write('${util.no_dots(node.sym.name)}_${method.name}($amp ')
g.expr(node.left)
g.writeln(');')

View File

@ -27,7 +27,7 @@ fn (mut g Gen) gen_embed_file_init(node ast.ComptimeCall) {
g.writeln('\t\t.free_compressed = 0,')
g.writeln('\t\t.free_uncompressed = 0,')
g.writeln('\t\t.len = $file_size')
g.writeln('} // $' + 'embed_file("$node.embed_file.apath")')
g.writeln('} // \$embed_file("$node.embed_file.apath")')
}
// gen_embedded_data embeds data into the V target executable.

View File

@ -43,20 +43,23 @@ fn (mut p Parser) hash() ast.HashStmt {
}
fn (mut p Parser) comp_call() ast.ComptimeCall {
err_node := ast.ComptimeCall{
scope: 0
}
p.check(.dollar)
error_msg := 'only `\$tmpl()`, `\$embed_file()` and `\$vweb.html()` comptime functions are supported right now'
if p.peek_tok.kind == .dot {
n := p.check_name() // skip `vweb.html()` TODO
if n != 'vweb' {
p.error(error_msg)
return ast.ComptimeCall{}
return err_node
}
p.check(.dot)
}
n := p.check_name() // (.name)
if n !in parser.supported_comptime_calls {
p.error(error_msg)
return ast.ComptimeCall{}
return err_node
}
is_embed_file := n == 'embed_file'
is_html := n == 'html'
@ -74,7 +77,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
if epath == '' {
p.error_with_pos('please supply a valid relative or absolute file path to the file to embed',
spos)
return ast.ComptimeCall{}
return err_node
}
if !p.pref.is_fmt {
abs_path := os.real_path(epath)
@ -85,12 +88,12 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
if !os.exists(epath) {
p.error_with_pos('"$epath" does not exist so it cannot be embedded',
spos)
return ast.ComptimeCall{}
return err_node
}
if !os.is_file(epath) {
p.error_with_pos('"$epath" is not a file so it cannot be embedded',
spos)
return ast.ComptimeCall{}
return err_node
}
} else {
epath = abs_path
@ -98,6 +101,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
}
p.register_auto_import('v.embed_file')
return ast.ComptimeCall{
scope: 0
is_embed: true
embed_file: ast.EmbeddedFile{
rpath: s
@ -126,11 +130,10 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
if !os.exists(path) {
if is_html {
p.error('vweb HTML template "$path" not found')
return ast.ComptimeCall{}
} else {
p.error('template file "$path" not found')
return ast.ComptimeCall{}
}
return err_node
}
// println('path is now "$path"')
}
@ -185,6 +188,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
}
}
return ast.ComptimeCall{
scope: 0
is_vweb: true
vweb_tmpl: file
method_name: n
@ -257,7 +261,9 @@ fn (mut p Parser) at() ast.AtExpr {
fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
p.check(.dollar)
if p.peek_tok.kind == .lpar {
method_pos := p.tok.position()
method_name := p.check_name()
p.mark_var_as_used(method_name)
// `app.$action()` (`action` is a string)
p.check(.lpar)
mut args_var := ''
@ -273,6 +279,8 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
return ast.ComptimeCall{
left: left
method_name: method_name
method_pos: method_pos
scope: p.scope
args_var: args_var
}
}

View File

@ -0,0 +1,26 @@
struct Test {}
fn (test Test) v() {
println('in v()')
}
fn (test Test) i() int {
return 4
}
fn (test Test) s() string {
return 'test'
}
fn (test Test) s2() string {
return 'Two'
}
fn test_call() {
test := Test{}
sv := 'v'
test.$sv()
si := 'i'
ri := test.$si()
assert ri == 4
ss := 's'
rs := test.$ss()
assert rs == 'test'
}