From 4e05e07b94596fae6350c04cebab5c10369c0c0b Mon Sep 17 00:00:00 2001 From: yuyi Date: Wed, 2 Nov 2022 21:41:44 +0800 Subject: [PATCH] checker: check undefined ident in closure anon fn (fix #16274) (#16278) --- vlib/v/checker/checker.v | 1 + vlib/v/checker/comptime.v | 3 ++ vlib/v/checker/fn.v | 3 ++ .../tests/closure_undefined_ident_err.out | 14 +++++++++ .../tests/closure_undefined_ident_err.vv | 31 +++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 vlib/v/checker/tests/closure_undefined_ident_err.out create mode 100644 vlib/v/checker/tests/closure_undefined_ident_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 91d693693f..1617b46e08 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -108,6 +108,7 @@ mut: files []ast.File expr_level int // to avoid infinite recursion segfaults due to compiler bugs cur_orm_ts ast.TypeSymbol + cur_anon_fn &ast.AnonFn = unsafe { nil } error_details []string vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path** loop_label string // set when inside a labelled for loop diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 5da06bf4ac..47212ce916 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -62,6 +62,9 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { return rtyp } if node.method_name == 'method' { + if c.inside_anon_fn && 'method' !in c.cur_anon_fn.inherited_vars.map(it.name) { + c.error('undefined ident `method` in the anonymous function', node.pos) + } for i, arg in node.args { // check each arg expression node.args[i].typ = c.expr(arg.expr) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index b2185a0b07..4bea8c92c5 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -404,9 +404,11 @@ fn (c Checker) check_same_type_ignoring_pointers(type_a ast.Type, type_b ast.Typ fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type { keep_fn := c.table.cur_fn keep_inside_anon := c.inside_anon_fn + keep_anon_fn := c.cur_anon_fn defer { c.table.cur_fn = keep_fn c.inside_anon_fn = keep_inside_anon + c.cur_anon_fn = keep_anon_fn } for param in node.decl.params { if param.name.len == 0 { @@ -415,6 +417,7 @@ fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type { } c.table.cur_fn = unsafe { &node.decl } c.inside_anon_fn = true + c.cur_anon_fn = unsafe { &node } mut has_generic := false for mut var in node.inherited_vars { parent_var := node.decl.scope.parent.find_var(var.name) or { diff --git a/vlib/v/checker/tests/closure_undefined_ident_err.out b/vlib/v/checker/tests/closure_undefined_ident_err.out new file mode 100644 index 0000000000..10faa97efa --- /dev/null +++ b/vlib/v/checker/tests/closure_undefined_ident_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/closure_undefined_ident_err.vv:25:7: warning: unused variable: `method` + 23 | c := Custom{'noname'} + 24 | + 25 | $for method in MyInterface.methods { + | ~~~~~~ + 26 | z := fn [c] () { + 27 | c.$method() +vlib/v/checker/tests/closure_undefined_ident_err.vv:27:6: error: undefined ident `method` in the anonymous function + 25 | $for method in MyInterface.methods { + 26 | z := fn [c] () { + 27 | c.$method() + | ~~~~~~~~~ + 28 | } + 29 | z() diff --git a/vlib/v/checker/tests/closure_undefined_ident_err.vv b/vlib/v/checker/tests/closure_undefined_ident_err.vv new file mode 100644 index 0000000000..0f7597ad1b --- /dev/null +++ b/vlib/v/checker/tests/closure_undefined_ident_err.vv @@ -0,0 +1,31 @@ +module main + +struct Custom { + name string +} + +interface MyInterface { + some_function() +} + +fn (c Custom) some_function() { + println('hello $c.name from ${@METHOD}') +} + +fn main() { + a := Custom{'rabbit'} + x := a.some_function + x() + b := Custom{'horse'} + y := b.some_function + y() + + c := Custom{'noname'} + + $for method in MyInterface.methods { + z := fn [c] () { + c.$method() + } + z() + } +}