checker: fix compile-time call with string identifier expression (#8415)
parent
058f3ba013
commit
43c3a3b080
|
@ -1130,6 +1130,8 @@ pub struct ComptimeCall {
|
||||||
pub:
|
pub:
|
||||||
has_parens bool // if $() is used, for vfmt
|
has_parens bool // if $() is used, for vfmt
|
||||||
method_name string
|
method_name string
|
||||||
|
method_pos token.Position
|
||||||
|
scope &Scope
|
||||||
left Expr
|
left Expr
|
||||||
is_vweb bool
|
is_vweb bool
|
||||||
vweb_tmpl File
|
vweb_tmpl File
|
||||||
|
@ -1137,7 +1139,8 @@ pub:
|
||||||
is_embed bool
|
is_embed bool
|
||||||
embed_file EmbeddedFile
|
embed_file EmbeddedFile
|
||||||
pub mut:
|
pub mut:
|
||||||
sym table.TypeSymbol
|
sym table.TypeSymbol
|
||||||
|
result_type table.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct None {
|
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
|
c.nr_errors += c2.nr_errors
|
||||||
}
|
}
|
||||||
if node.method_name == 'html' {
|
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 node.is_vweb || node.method_name == 'method' {
|
||||||
if mut var is ast.Var {
|
return table.string_type
|
||||||
var.is_used = true
|
|
||||||
c.fn_scope.objects[node.method_name] = var
|
|
||||||
}
|
}
|
||||||
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 {
|
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() {
|
fn main() {
|
||||||
test := Test{}
|
test := Test{}
|
||||||
abc := 'print'
|
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
|
return
|
||||||
}
|
}
|
||||||
g.writeln('// $' + 'method call. sym="$node.sym.name"')
|
g.writeln('// \$method call. sym="$node.sym.name"')
|
||||||
mut j := 0
|
|
||||||
result_type := g.table.find_type_idx('vweb.Result') // TODO not just vweb
|
|
||||||
if node.method_name == 'method' {
|
if node.method_name == 'method' {
|
||||||
// `app.$method()`
|
// `app.$method()`
|
||||||
m := node.sym.find_method(g.comp_for_method) or { return }
|
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')
|
g.write(' ); // vweb action call with args')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
mut j := 0
|
||||||
for method in node.sym.methods {
|
for method in node.sym.methods {
|
||||||
// if method.return_type != table.void_type {
|
// if method.return_type != table.void_type {
|
||||||
if method.return_type != result_type {
|
if method.return_type != node.result_type {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if method.params.len != 1 {
|
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')
|
// p.error('`$p.expr_var.name` needs to be a reference')
|
||||||
// }
|
// }
|
||||||
amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
|
amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
|
||||||
if j > 0 {
|
if node.is_vweb {
|
||||||
g.write(' else ')
|
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.write('${util.no_dots(node.sym.name)}_${method.name}($amp ')
|
||||||
g.expr(node.left)
|
g.expr(node.left)
|
||||||
g.writeln(');')
|
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_compressed = 0,')
|
||||||
g.writeln('\t\t.free_uncompressed = 0,')
|
g.writeln('\t\t.free_uncompressed = 0,')
|
||||||
g.writeln('\t\t.len = $file_size')
|
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.
|
// 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 {
|
fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
|
err_node := ast.ComptimeCall{
|
||||||
|
scope: 0
|
||||||
|
}
|
||||||
p.check(.dollar)
|
p.check(.dollar)
|
||||||
error_msg := 'only `\$tmpl()`, `\$embed_file()` and `\$vweb.html()` comptime functions are supported right now'
|
error_msg := 'only `\$tmpl()`, `\$embed_file()` and `\$vweb.html()` comptime functions are supported right now'
|
||||||
if p.peek_tok.kind == .dot {
|
if p.peek_tok.kind == .dot {
|
||||||
n := p.check_name() // skip `vweb.html()` TODO
|
n := p.check_name() // skip `vweb.html()` TODO
|
||||||
if n != 'vweb' {
|
if n != 'vweb' {
|
||||||
p.error(error_msg)
|
p.error(error_msg)
|
||||||
return ast.ComptimeCall{}
|
return err_node
|
||||||
}
|
}
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
}
|
}
|
||||||
n := p.check_name() // (.name)
|
n := p.check_name() // (.name)
|
||||||
if n !in parser.supported_comptime_calls {
|
if n !in parser.supported_comptime_calls {
|
||||||
p.error(error_msg)
|
p.error(error_msg)
|
||||||
return ast.ComptimeCall{}
|
return err_node
|
||||||
}
|
}
|
||||||
is_embed_file := n == 'embed_file'
|
is_embed_file := n == 'embed_file'
|
||||||
is_html := n == 'html'
|
is_html := n == 'html'
|
||||||
|
@ -74,7 +77,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
if epath == '' {
|
if epath == '' {
|
||||||
p.error_with_pos('please supply a valid relative or absolute file path to the file to embed',
|
p.error_with_pos('please supply a valid relative or absolute file path to the file to embed',
|
||||||
spos)
|
spos)
|
||||||
return ast.ComptimeCall{}
|
return err_node
|
||||||
}
|
}
|
||||||
if !p.pref.is_fmt {
|
if !p.pref.is_fmt {
|
||||||
abs_path := os.real_path(epath)
|
abs_path := os.real_path(epath)
|
||||||
|
@ -85,12 +88,12 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
if !os.exists(epath) {
|
if !os.exists(epath) {
|
||||||
p.error_with_pos('"$epath" does not exist so it cannot be embedded',
|
p.error_with_pos('"$epath" does not exist so it cannot be embedded',
|
||||||
spos)
|
spos)
|
||||||
return ast.ComptimeCall{}
|
return err_node
|
||||||
}
|
}
|
||||||
if !os.is_file(epath) {
|
if !os.is_file(epath) {
|
||||||
p.error_with_pos('"$epath" is not a file so it cannot be embedded',
|
p.error_with_pos('"$epath" is not a file so it cannot be embedded',
|
||||||
spos)
|
spos)
|
||||||
return ast.ComptimeCall{}
|
return err_node
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
epath = abs_path
|
epath = abs_path
|
||||||
|
@ -98,6 +101,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
}
|
}
|
||||||
p.register_auto_import('v.embed_file')
|
p.register_auto_import('v.embed_file')
|
||||||
return ast.ComptimeCall{
|
return ast.ComptimeCall{
|
||||||
|
scope: 0
|
||||||
is_embed: true
|
is_embed: true
|
||||||
embed_file: ast.EmbeddedFile{
|
embed_file: ast.EmbeddedFile{
|
||||||
rpath: s
|
rpath: s
|
||||||
|
@ -126,11 +130,10 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
if !os.exists(path) {
|
if !os.exists(path) {
|
||||||
if is_html {
|
if is_html {
|
||||||
p.error('vweb HTML template "$path" not found')
|
p.error('vweb HTML template "$path" not found')
|
||||||
return ast.ComptimeCall{}
|
|
||||||
} else {
|
} else {
|
||||||
p.error('template file "$path" not found')
|
p.error('template file "$path" not found')
|
||||||
return ast.ComptimeCall{}
|
|
||||||
}
|
}
|
||||||
|
return err_node
|
||||||
}
|
}
|
||||||
// println('path is now "$path"')
|
// println('path is now "$path"')
|
||||||
}
|
}
|
||||||
|
@ -185,6 +188,7 @@ fn (mut p Parser) comp_call() ast.ComptimeCall {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ast.ComptimeCall{
|
return ast.ComptimeCall{
|
||||||
|
scope: 0
|
||||||
is_vweb: true
|
is_vweb: true
|
||||||
vweb_tmpl: file
|
vweb_tmpl: file
|
||||||
method_name: n
|
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 {
|
fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
|
||||||
p.check(.dollar)
|
p.check(.dollar)
|
||||||
if p.peek_tok.kind == .lpar {
|
if p.peek_tok.kind == .lpar {
|
||||||
|
method_pos := p.tok.position()
|
||||||
method_name := p.check_name()
|
method_name := p.check_name()
|
||||||
|
p.mark_var_as_used(method_name)
|
||||||
// `app.$action()` (`action` is a string)
|
// `app.$action()` (`action` is a string)
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
mut args_var := ''
|
mut args_var := ''
|
||||||
|
@ -273,6 +279,8 @@ fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
|
||||||
return ast.ComptimeCall{
|
return ast.ComptimeCall{
|
||||||
left: left
|
left: left
|
||||||
method_name: method_name
|
method_name: method_name
|
||||||
|
method_pos: method_pos
|
||||||
|
scope: p.scope
|
||||||
args_var: args_var
|
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