checker: fix compile-time call with string identifier expression (#8415)
parent
058f3ba013
commit
43c3a3b080
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 | }
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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(');')
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
}
|
Loading…
Reference in New Issue