From 21ef3c6e5972bbe8ab61195f98ccb85048b78985 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 27 Oct 2025 18:07:52 +0000 Subject: [PATCH] patch 9.1.1882: Vim9: Not able to use a lambda with :defer Problem: Vim9: Not able to use a lambda with :defer (Maxim Kim) Solution: Add support for this (Yegappan Lakshmanan) fixes: #18626 closes: #18643 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- runtime/doc/userfunc.txt | 11 +++- src/proto/vim9expr.pro | 1 + src/testdir/test_vim9_script.vim | 96 ++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9cmds.c | 42 ++++++++------ src/vim9expr.c | 2 +- 6 files changed, 136 insertions(+), 18 deletions(-) diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index 5b3605a147..0f008446ee 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -1,4 +1,4 @@ -*userfunc.txt* For Vim version 9.1. Last change: 2025 Oct 12 +*userfunc.txt* For Vim version 9.1. Last change: 2025 Oct 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -449,6 +449,15 @@ or altering execution outside of deferred functions. No range is accepted. The function can be a partial with extra arguments, but not with a dictionary. *E1300* +In a |:def| function, a lambda can be used with |:defer|. Example: > + + def Fn() + set lazyredraw + defer () => { + set lazyredraw& + }() + enddef +< ============================================================================== 4. Automatically loading functions ~ diff --git a/src/proto/vim9expr.pro b/src/proto/vim9expr.pro index 58c79fc027..4de1439179 100644 --- a/src/proto/vim9expr.pro +++ b/src/proto/vim9expr.pro @@ -7,6 +7,7 @@ int compile_load(char_u **arg, size_t namelen, char_u *end_arg, cctx_T *cctx, in int compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, ca_special_T special_fn); char_u *to_name_end(char_u *arg, int use_namespace); char_u *to_name_const_end(char_u *arg); +int compile_lambda(char_u **arg, cctx_T *cctx); int get_lambda_tv_and_compile(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg); exprtype_T get_compare_type(char_u *p, int *len, int *type_is); void skip_expr_cctx(char_u **arg, cctx_T *cctx); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 40832a606c..0134956c30 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5249,6 +5249,102 @@ def Test_defer_lambda_funcref() v9.CheckSourceSuccess(lines) enddef +" Test for using defer with a lambda and a command block +def Test_defer_lambda_func() + var lines =<< trim END + vim9script + var result = '' + def Foo() + result = 'xxx' + defer (a: number, b: string): number => { + result = $'{a}:{b}' + return 0 + }(10, 'aaa') + result = 'yyy' + enddef + Foo() + assert_equal('10:aaa', result) + END + v9.CheckScriptSuccess(lines) + + # Error: argument type mismatch + lines =<< trim END + vim9script + def Foo() + defer (a: number, b: string): number => { + return 0 + }(10, 20) + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected string but got number', 1) + + # Error: not enough arguments + lines =<< trim END + vim9script + def Foo() + defer (a: number) => { + }() + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E119: Not enough arguments for function: (a: number) => {', 1) + + # Error: too many arguments + lines =<< trim END + vim9script + def Foo() + defer () => { + }(10) + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E118: Too many arguments for function: () => {', 1) + + # Error: invalid command in command-block + lines =<< trim END + vim9script + def Foo() + defer () => { + xxx + }() + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E476: Invalid command: xxx', 1) + + # Error: missing return + lines =<< trim END + vim9script + def Foo() + defer (): number => { + }() + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1027: Missing return statement', 1) + + # Error: missing lambda body + lines =<< trim END + vim9script + def Foo() + defer (a: number): number + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1028: Compiling :def function failed', 1) + + # Error: invalid lambda syntax + lines =<< trim END + vim9script + def Foo() + defer ( + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1028: Compiling :def function failed', 1) +enddef + " Test for using an non-existing type in a "for" statement. def Test_invalid_type_in_for() var lines =<< trim END diff --git a/src/version.c b/src/version.c index b837e88f73..ed9059da4a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1882, /**/ 1881, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index fb9f78137b..684d2ee66d 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -2040,25 +2040,35 @@ compile_defer(char_u *arg_start, cctx_T *cctx) int argcount = 0; int defer_var_idx; type_T *type = NULL; - int func_idx; + int func_idx = -1; - // Get a funcref for the function name. - // TODO: better way to find the "(". - paren = vim_strchr(arg, '('); - if (paren == NULL) + if (*arg == '(') { - semsg(_(e_missing_parenthesis_str), arg); - return NULL; + // a lambda function + if (compile_lambda(&arg, cctx) != OK) + return NULL; + paren = arg; + } + else + { + // Get a funcref for the function name. + // TODO: better way to find the "(". + paren = vim_strchr(arg, '('); + if (paren == NULL) + { + semsg(_(e_missing_parenthesis_str), arg); + return NULL; + } + *paren = NUL; + func_idx = find_internal_func(arg); + if (func_idx >= 0) + // TODO: better type + generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx), + &t_func_any, FALSE); + else if (compile_expr0(&arg, cctx) == FAIL) + return NULL; + *paren = '('; } - *paren = NUL; - func_idx = find_internal_func(arg); - if (func_idx >= 0) - // TODO: better type - generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx), - &t_func_any, FALSE); - else if (compile_expr0(&arg, cctx) == FAIL) - return NULL; - *paren = '('; // check for function type if (cctx->ctx_skip != SKIP_YES) diff --git a/src/vim9expr.c b/src/vim9expr.c index ab14afc335..f692eeff30 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -1747,7 +1747,7 @@ compile_tuple( * "*arg" points to the '('. * Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda. */ - static int + int compile_lambda(char_u **arg, cctx_T *cctx) { int r; -- 2.47.3