# HG changeset patch # User Bram Moolenaar # Date 1618068603 -7200 # Node ID 96e0b898d5b4db57e39cf851bda9eaf569dce1a4 # Parent 31c7a2a80f9cc90158c81cd6da0c7ff7476ff46e patch 8.2.2744: Vim9: no way to explicitly ignore an argument Commit: https://github.com/vim/vim/commit/962c43bf0d6a33b905f2acd920d3701476ebb5c9 Author: Bram Moolenaar Date: Sat Apr 10 17:18:09 2021 +0200 patch 8.2.2744: Vim9: no way to explicitly ignore an argument Problem: Vim9: no way to explicitly ignore an argument. Solution: Use the underscore as the name for an ignored argument. diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -137,18 +137,21 @@ arguments). Vim9 functions ~ A function defined with `:def` is compiled. Execution is many times faster, -often 10x to 100x times. +often 10 to 100 times. Many errors are already found when compiling, before the function is executed. The syntax is strict, to enforce code that is easy to read and understand. -Compilation is done when either of these is encountered: +Compilation is done when any of these is encountered: - the first time the function is called -- when the `:defcompile` command is encountered in the script where the +- when the `:defcompile` command is encountered in the script after the function was defined - `:disassemble` is used for the function. - a function that is compiled calls the function or uses it as a function reference + *E1091* +If compilation fails it is not tried again on the next call, instead this +error is given: "E1091: Function is not compiled: {name}". `:def` has no options like `:function` does: "range", "abort", "dict" or "closure". A `:def` function always aborts on an error (unless `:silent!` was @@ -161,7 +164,7 @@ functions. Arguments are accessed by name, without "a:", just like any other language. There is no "a:" dictionary or "a:000" list. - + *vim9-variable-arguments* Variable arguments are defined as the last argument, with a name and have a list type, similar to TypeScript. For example, a list of numbers: > def MyFunc(...itemlist: list) @@ -176,6 +179,15 @@ should use its default value. Example: ... enddef MyFunc(v:none, 'LAST') # first argument uses default value 'one' +< + *vim9-ignored-argument* +The argument "_" (an underscore) can be used to ignore the argument. This is +most useful in callbacks where you don't need it, but do need to give an +argument to match the call. E.g. when using map() two arguments are passed, +the key and the value, to ignore the key: > + map(myList, (_, v) => v * 2) +There is no error for using the "_" argument multiple times. No type needs to +be given. Functions and variables are script-local by default ~ @@ -433,6 +445,15 @@ But you can use a backslash to concatena filter(list, (k, \ v) \ => v > 0) +< *vim9-lambda-arguments* +In legacy script a lambda could be called with any number of extra arguments, +there was no way to warn for not using them. In Vim9 script the number of +arguments must match. If you do want to accept any arguments, or any further +arguments, use "..._", which makes the function accept +|vim9-variable-arguments|. Example: > + var Callback = (..._) => 'anything' + echo Callback(1, 2, 3) # displays "anything" + < *inline-function* Additionally, a lambda can contain statements in {}: > var Lambda = (arg) => { diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -397,3 +397,5 @@ EXTERN char e_failed_to_extract_pwd_from INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7")); EXTERN char e_variable_arguments_type_must_be_list_str[] INIT(= N_("E1180: Variable arguments type must be a list: %s")); +EXTERN char e_cannot_use_underscore_here[] + INIT(= N_("E1181: Cannot use an underscore here")); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -3514,7 +3514,12 @@ eval7( { int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') + if (in_vim9script() && len == 1 && *s == '_') + { + emsg(_(e_cannot_use_underscore_here)); + ret = FAIL; + } + else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') { // "name(..." recursive! *arg = skipwhite(*arg); diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3188,6 +3188,11 @@ set_var_const( goto failed; } var_in_vim9script = is_script_local && current_script_is_vim9(); + if (var_in_vim9script && name[0] == '_' && name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto failed; + } di = find_var_in_ht(ht, 0, varname, TRUE); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2619,6 +2619,41 @@ def Test_compile_error() delfunc g:Broken enddef +def Test_ignored_argument() + var lines =<< trim END + vim9script + def Ignore(_, _): string + return 'yes' + enddef + assert_equal('yes', Ignore(1, 2)) + + func Ok(_) + return a:_ + endfunc + assert_equal('ok', Ok('ok')) + + func Oktoo() + let _ = 'too' + return _ + endfunc + assert_equal('too', Oktoo()) + END + CheckScriptSuccess(lines) + + lines =<< trim END + def Ignore(_: string): string + return _ + enddef + defcompile + END + CheckScriptFailure(lines, 'E1181:', 1) + + lines =<< trim END + var _ = 1 + END + CheckDefAndScriptFailure(lines, 'E1181:', 1) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2744, +/**/ 2743, /**/ 2742, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4416,6 +4416,12 @@ compile_expr7( // "name" or "name()" p = to_name_end(*arg, TRUE); + if (p - *arg == (size_t)1 && **arg == '_') + { + emsg(_(e_cannot_use_underscore_here)); + return FAIL; + } + if (*p == '(') { r = compile_call(arg, p - *arg, cctx, ppconst, 0); @@ -6378,6 +6384,11 @@ compile_assignment(char_u *arg, exarg_T semsg(_(e_cannot_assign_to_constant), lhs.lhs_name); goto theend; } + if (is_decl && lhs.lhs_name[0] == '_' && lhs.lhs_name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto theend; + } if (!heredoc) {