From f5bf18c79a3876f115f6e2556dd1cceab5308dc2 Mon Sep 17 00:00:00 2001 From: shove Date: Wed, 15 Nov 2023 04:34:16 +0800 Subject: [PATCH] parser, checker: fix var scope in lambda(fix #19860) (#19871) --- vlib/v/checker/checker.v | 14 ++++++-- .../tests/lambda_undefined_variables_err.out | 33 +++++++++++++++++++ .../tests/lambda_undefined_variables_err.vv | 9 +++++ vlib/v/parser/expr.v | 1 + 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 vlib/v/checker/tests/lambda_undefined_variables_err.out create mode 100644 vlib/v/checker/tests/lambda_undefined_variables_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8b88571872..bd2f7f5e49 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -79,6 +79,7 @@ pub mut: inside_unsafe bool // true inside `unsafe {}` blocks inside_const bool // true inside `const ( ... )` blocks inside_anon_fn bool // true inside `fn() { ... }()` + inside_lambda bool // true inside `|...| ...` inside_ref_lit bool // true inside `a := &something` inside_defer bool // true inside `defer {}` blocks inside_fn_arg bool // `a`, `b` in `a.f(b)` @@ -2799,6 +2800,10 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type { return c.int_lit(mut node) } ast.LambdaExpr { + c.inside_lambda = true + defer { + c.inside_lambda = false + } return c.lambda_expr(mut node, c.expected_type) } ast.LockExpr { @@ -3732,8 +3737,13 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { found_var := c.fn_scope.find_var(node.name) if found_var != none { - c.error('`${node.name}` must be added to the capture list for the closure to be used inside', - node.pos) + if c.inside_lambda { + // Lambdas don't support capturing variables yet, so that's the only hint. + c.error('undefined variable `${node.name}`', node.pos) + } else { + c.error('`${node.name}` must be added to the capture list for the closure to be used inside', + node.pos) + } return ast.void_type } } diff --git a/vlib/v/checker/tests/lambda_undefined_variables_err.out b/vlib/v/checker/tests/lambda_undefined_variables_err.out new file mode 100644 index 0000000000..ba91e8931c --- /dev/null +++ b/vlib/v/checker/tests/lambda_undefined_variables_err.out @@ -0,0 +1,33 @@ +vlib/v/checker/tests/lambda_undefined_variables_err.vv:7:2: warning: unused variable: `s2` + 5 | f(|x| s1) + 6 | + 7 | s2 := 'abc' + | ~~ + 8 | f(|x| s2) + 9 | } +vlib/v/checker/tests/lambda_undefined_variables_err.vv:5:8: error: undefined ident: `s1` + 3 | + 4 | fn main() { + 5 | f(|x| s1) + | ~~ + 6 | + 7 | s2 := 'abc' +vlib/v/checker/tests/lambda_undefined_variables_err.vv:5:4: error: `s1` used as value + 3 | + 4 | fn main() { + 5 | f(|x| s1) + | ^ + 6 | + 7 | s2 := 'abc' +vlib/v/checker/tests/lambda_undefined_variables_err.vv:8:8: error: undefined variable `s2` + 6 | + 7 | s2 := 'abc' + 8 | f(|x| s2) + | ~~ + 9 | } +vlib/v/checker/tests/lambda_undefined_variables_err.vv:8:4: error: `s2` used as value + 6 | + 7 | s2 := 'abc' + 8 | f(|x| s2) + | ^ + 9 | } diff --git a/vlib/v/checker/tests/lambda_undefined_variables_err.vv b/vlib/v/checker/tests/lambda_undefined_variables_err.vv new file mode 100644 index 0000000000..aee19868d0 --- /dev/null +++ b/vlib/v/checker/tests/lambda_undefined_variables_err.vv @@ -0,0 +1,9 @@ +fn f(g fn (int) string) { +} + +fn main() { + f(|x| s1) + + s2 := 'abc' + f(|x| s2) +} diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 7d772b9d79..cdff14ddf4 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -852,6 +852,7 @@ fn (mut p Parser) lambda_expr() ?ast.LambdaExpr { defer { p.close_scope() } + p.scope.detached_from_parent = true mut pos := p.tok.pos() mut params := []ast.Ident{}