]> granicus.if.org Git - vim/commitdiff
patch 8.2.2635: Vim9: cannot define an inline function v8.2.2635
authorBram Moolenaar <Bram@vim.org>
Sun, 21 Mar 2021 19:53:29 +0000 (20:53 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 21 Mar 2021 19:53:29 +0000 (20:53 +0100)
Problem:    Vim9: cannot define an inline function.
Solution:   Make an inline function mostly work.

src/errors.h
src/misc2.c
src/proto/vim9compile.pro
src/testdir/test_vim9_expr.vim
src/userfunc.c
src/version.c
src/vim9compile.c

index e5c9d8e9926ea9e39c5af8d62cb4b7e69f96be69..300802034f9d46af7f55ca3b1030925be8952e3c 100644 (file)
@@ -377,3 +377,7 @@ EXTERN char e_import_as_name_not_supported_here[]
        INIT(= N_("E1169: 'import * as {name}' not supported here"));
 EXTERN char e_cannot_use_hash_curly_to_start_comment[]
        INIT(= N_("E1170: Cannot use #{ to start a comment"));
+EXTERN char e_missing_end_block[]
+       INIT(= N_("E1171: Missing } after inline function"));
+EXTERN char e_cannot_use_default_values_in_lambda[]
+       INIT(= N_("E1172: Cannot use default values in a lambda"));
index 70a19c127df390f3532d6aa961bfa557c7bcd227..90b8b5893d3bd89dbbae1b5997d6bfb286a2fbfc 100644 (file)
@@ -2026,8 +2026,9 @@ ga_clear_strings(garray_T *gap)
 {
     int                i;
 
-    for (i = 0; i < gap->ga_len; ++i)
-       vim_free(((char_u **)(gap->ga_data))[i]);
+    if (gap->ga_data != NULL)
+       for (i = 0; i < gap->ga_len; ++i)
+           vim_free(((char_u **)(gap->ga_data))[i]);
     ga_clear(gap);
 }
 
index 4f2fffe686f84969f8a746c904ea80a2a5f5b96d..05d399867b57ef86f363d4ee8407f8a18c619c63 100644 (file)
@@ -14,6 +14,7 @@ char_u *to_name_end(char_u *arg, int use_namespace);
 char_u *to_name_const_end(char_u *arg);
 exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
 void error_white_both(char_u *op, int len);
+void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
 int assignment_len(char_u *p, int *heredoc);
 void vim9_declare_error(char_u *name);
 int check_vim9_unlet(char_u *name);
index afe5f7dab0b625876da33174e3d9d84569b14e7b..6b9b54a58f95d6b7cb485a6927421948bf4c2529 100644 (file)
@@ -1946,6 +1946,25 @@ def Test_expr7_lambda()
   CheckScriptSuccess(lines)
 enddef
 
+def Test_expr7_lambda_block()
+  var lines =<< trim END
+      var Func = (s: string): string => {
+                      return 'hello ' .. s
+                    }
+      assert_equal('hello there', Func('there'))
+
+      var ll = range(3)
+      var dll = mapnew(ll, (k, v): string => {
+          if v % 2
+            return 'yes'
+          endif
+          return 'no'
+        })
+      assert_equal(['no', 'yes', 'no'], dll)
+  END
+  CheckDefAndScriptSuccess(lines)
+enddef
+
 def NewLambdaWithComments(): func
   return (x) =>
             # some comment
index 4793ee555285c7c50c4c7a5c9d688582aaf396f8..51894dc101fea874a7d114befff2879b949a53a0 100644 (file)
@@ -397,6 +397,25 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
     return OK;
 }
 
+    static int
+parse_return_type(ufunc_T *fp, char_u *ret_type)
+{
+    if (ret_type == NULL)
+       fp->uf_ret_type = &t_void;
+    else
+    {
+       char_u *p = ret_type;
+
+       fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
+       if (fp->uf_ret_type == NULL)
+       {
+           fp->uf_ret_type = &t_void;
+           return FAIL;
+       }
+    }
+    return OK;
+}
+
 /*
  * Register function "fp" as using "current_funccal" as its scope.
  */
@@ -516,27 +535,521 @@ skip_arrow(
                return NULL;
            }
        }
-       bef = s;
-       s = skipwhite(s);
-       if (*s != '=')
-           return NULL;
-       ++s;
+       bef = s;
+       s = skipwhite(s);
+       if (*s != '=')
+           return NULL;
+       ++s;
+    }
+    if (*s != '>')
+       return NULL;
+    if (white_error != NULL && ((!VIM_ISWHITE(*bef) && *bef != '{')
+               || !IS_WHITE_OR_NUL(s[1])))
+    {
+       *white_error = TRUE;
+       semsg(_(e_white_space_required_before_and_after_str_at_str),
+                                              equal_arrow ? "=>" : "->", bef);
+       return NULL;
+    }
+    return skipwhite(s + 1);
+}
+
+/*
+ * Check if "*cmd" points to a function command and if so advance "*cmd" and
+ * return TRUE.
+ * Otherwise return FALSE;
+ * Do not consider "function(" to be a command.
+ */
+    static int
+is_function_cmd(char_u **cmd)
+{
+    char_u *p = *cmd;
+
+    if (checkforcmd(&p, "function", 2))
+    {
+       if (*p == '(')
+           return FALSE;
+       *cmd = p;
+       return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * Read the body of a function, put every line in "newlines".
+ * "newlines" must already have been initialized.
+ * "eap->cmdidx" is CMD_function, CMD_def or CMD_block;
+ */
+    static int
+get_function_body(
+       exarg_T     *eap,
+       garray_T    *newlines,
+       char_u      *line_arg_in,
+       char_u      **line_to_free)
+{
+    linenr_T   sourcing_lnum_top = SOURCING_LNUM;
+    linenr_T   sourcing_lnum_off;
+    int                saved_wait_return = need_wait_return;
+    char_u     *line_arg = line_arg_in;
+    int                vim9_function = eap->cmdidx == CMD_def
+                                                  || eap->cmdidx == CMD_block;
+#define MAX_FUNC_NESTING 50
+    char       nesting_def[MAX_FUNC_NESTING];
+    int                nesting = 0;
+    getline_opt_T getline_options;
+    int                indent = 2;
+    char_u     *skip_until = NULL;
+    int                ret = FAIL;
+    int                is_heredoc = FALSE;
+    char_u     *heredoc_trimmed = NULL;
+
+    // Detect having skipped over comment lines to find the return
+    // type.  Add NULL lines to keep the line count correct.
+    sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
+    if (SOURCING_LNUM < sourcing_lnum_off)
+    {
+       sourcing_lnum_off -= SOURCING_LNUM;
+       if (ga_grow(newlines, sourcing_lnum_off) == FAIL)
+           goto theend;
+       while (sourcing_lnum_off-- > 0)
+           ((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
+    }
+
+    nesting_def[nesting] = vim9_function;
+    getline_options = vim9_function
+                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
+    for (;;)
+    {
+       char_u  *theline;
+       char_u  *p;
+       char_u  *arg;
+
+       if (KeyTyped)
+       {
+           msg_scroll = TRUE;
+           saved_wait_return = FALSE;
+       }
+       need_wait_return = FALSE;
+
+       if (line_arg != NULL)
+       {
+           // Use eap->arg, split up in parts by line breaks.
+           theline = line_arg;
+           p = vim_strchr(theline, '\n');
+           if (p == NULL)
+               line_arg += STRLEN(line_arg);
+           else
+           {
+               *p = NUL;
+               line_arg = p + 1;
+           }
+       }
+       else
+       {
+           vim_free(*line_to_free);
+           if (eap->getline == NULL)
+               theline = getcmdline(':', 0L, indent, getline_options);
+           else
+               theline = eap->getline(':', eap->cookie, indent,
+                                                             getline_options);
+           *line_to_free = theline;
+       }
+       if (KeyTyped)
+           lines_left = Rows - 1;
+       if (theline == NULL)
+       {
+           // Use the start of the function for the line number.
+           SOURCING_LNUM = sourcing_lnum_top;
+           if (skip_until != NULL)
+               semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+           else if (eap->cmdidx == CMD_def)
+               emsg(_(e_missing_enddef));
+           else if (eap->cmdidx == CMD_block)
+               emsg(_(e_missing_end_block));
+           else
+               emsg(_("E126: Missing :endfunction"));
+           goto theend;
+       }
+
+       // Detect line continuation: SOURCING_LNUM increased more than one.
+       sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
+       if (SOURCING_LNUM < sourcing_lnum_off)
+           sourcing_lnum_off -= SOURCING_LNUM;
+       else
+           sourcing_lnum_off = 0;
+
+       if (skip_until != NULL)
+       {
+           // Don't check for ":endfunc"/":enddef" between
+           // * ":append" and "."
+           // * ":python <<EOF" and "EOF"
+           // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
+           if (heredoc_trimmed == NULL
+                   || (is_heredoc && skipwhite(theline) == theline)
+                   || STRNCMP(theline, heredoc_trimmed,
+                                                STRLEN(heredoc_trimmed)) == 0)
+           {
+               if (heredoc_trimmed == NULL)
+                   p = theline;
+               else if (is_heredoc)
+                   p = skipwhite(theline) == theline
+                                ? theline : theline + STRLEN(heredoc_trimmed);
+               else
+                   p = theline + STRLEN(heredoc_trimmed);
+               if (STRCMP(p, skip_until) == 0)
+               {
+                   VIM_CLEAR(skip_until);
+                   VIM_CLEAR(heredoc_trimmed);
+                   getline_options = vim9_function
+                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
+                   is_heredoc = FALSE;
+               }
+           }
+       }
+       else
+       {
+           int c;
+
+           // skip ':' and blanks
+           for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
+               ;
+
+           // Check for "endfunction", "enddef" or "}".
+           // When a ":" follows it must be a dict key; "enddef: value,"
+           if ((nesting == 0 && eap->cmdidx == CMD_block)
+                   ? *p == '}'
+                   : (checkforcmd(&p, nesting_def[nesting]
+                                               ? "enddef" : "endfunction", 4)
+                       && *p != ':'))
+           {
+               if (nesting-- == 0)
+               {
+                   char_u *nextcmd = NULL;
+
+                   if (*p == '|' || *p == '}')
+                       nextcmd = p + 1;
+                   else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
+                       nextcmd = line_arg;
+                   else if (*p != NUL && *p != (vim9_function ? '#' : '"')
+                                       && p_verbose > 0
+                                       && eap->cmdidx != CMD_block)
+                       give_warning2(eap->cmdidx == CMD_def
+                           ? (char_u *)_("W1001: Text found after :enddef: %s")
+                           : (char_u *)_("W22: Text found after :endfunction: %s"),
+                            p, TRUE);
+                   if (nextcmd != NULL)
+                   {
+                       // Another command follows. If the line came from "eap"
+                       // we can simply point into it, otherwise we need to
+                       // change "eap->cmdlinep".
+                       eap->nextcmd = nextcmd;
+                       if (*line_to_free != NULL)
+                       {
+                           vim_free(*eap->cmdlinep);
+                           *eap->cmdlinep = *line_to_free;
+                           *line_to_free = NULL;
+                       }
+                   }
+                   break;
+               }
+           }
+
+           // Check for mismatched "endfunc" or "enddef".
+           // We don't check for "def" inside "func" thus we also can't check
+           // for "enddef".
+           // We continue to find the end of the function, although we might
+           // not find it.
+           else if (nesting_def[nesting])
+           {
+               if (checkforcmd(&p, "endfunction", 4) && *p != ':')
+                   emsg(_(e_mismatched_endfunction));
+           }
+           else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
+               emsg(_(e_mismatched_enddef));
+
+           // Increase indent inside "if", "while", "for" and "try", decrease
+           // at "end".
+           if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
+               indent -= 2;
+           else if (STRNCMP(p, "if", 2) == 0
+                   || STRNCMP(p, "wh", 2) == 0
+                   || STRNCMP(p, "for", 3) == 0
+                   || STRNCMP(p, "try", 3) == 0)
+               indent += 2;
+
+           // Check for defining a function inside this function.
+           // Only recognize "def" inside "def", not inside "function",
+           // For backwards compatibility, see Test_function_python().
+           c = *p;
+           if (is_function_cmd(&p)
+                   || (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
+           {
+               if (*p == '!')
+                   p = skipwhite(p + 1);
+               p += eval_fname_script(p);
+               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
+                                                                 NULL, NULL));
+               if (*skipwhite(p) == '(')
+               {
+                   if (nesting == MAX_FUNC_NESTING - 1)
+                       emsg(_(e_function_nesting_too_deep));
+                   else
+                   {
+                       ++nesting;
+                       nesting_def[nesting] = (c == 'd');
+                       indent += 2;
+                   }
+               }
+           }
+
+           // Check for ":append", ":change", ":insert".  Not for :def.
+           p = skip_range(p, FALSE, NULL);
+           if (!vim9_function
+               && ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
+                   || (p[0] == 'c'
+                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
+                               && (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
+                                       && (STRNCMP(&p[3], "nge", 3) != 0
+                                           || !ASCII_ISALPHA(p[6])))))))
+                   || (p[0] == 'i'
+                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
+                               && (!ASCII_ISALPHA(p[2])
+                                   || (p[2] == 's'
+                                       && (!ASCII_ISALPHA(p[3])
+                                               || p[3] == 'e'))))))))
+               skip_until = vim_strsave((char_u *)".");
+
+           // Check for ":python <<EOF", ":tcl <<EOF", etc.
+           arg = skipwhite(skiptowhite(p));
+           if (arg[0] == '<' && arg[1] =='<'
+                   && ((p[0] == 'p' && p[1] == 'y'
+                                   && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
+                                       || ((p[2] == '3' || p[2] == 'x')
+                                                  && !ASCII_ISALPHA(p[3]))))
+                       || (p[0] == 'p' && p[1] == 'e'
+                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
+                       || (p[0] == 't' && p[1] == 'c'
+                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
+                       || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
+                                   && !ASCII_ISALPHA(p[3]))
+                       || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
+                                   && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
+                       || (p[0] == 'm' && p[1] == 'z'
+                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
+                       ))
+           {
+               // ":python <<" continues until a dot, like ":append"
+               p = skipwhite(arg + 2);
+               if (STRNCMP(p, "trim", 4) == 0)
+               {
+                   // Ignore leading white space.
+                   p = skipwhite(p + 4);
+                   heredoc_trimmed = vim_strnsave(theline,
+                                                skipwhite(theline) - theline);
+               }
+               if (*p == NUL)
+                   skip_until = vim_strsave((char_u *)".");
+               else
+                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
+               getline_options = GETLINE_NONE;
+               is_heredoc = TRUE;
+           }
+
+           // Check for ":cmd v =<< [trim] EOF"
+           //       and ":cmd [a, b] =<< [trim] EOF"
+           //       and "lines =<< [trim] EOF" for Vim9
+           // Where "cmd" can be "let", "var", "final" or "const".
+           arg = skipwhite(skiptowhite(p));
+           if (*arg == '[')
+               arg = vim_strchr(arg, ']');
+           if (arg != NULL)
+           {
+               int found = (eap->cmdidx == CMD_def && arg[0] == '='
+                                            && arg[1] == '<' && arg[2] =='<');
+
+               if (!found)
+                   // skip over the argument after "cmd"
+                   arg = skipwhite(skiptowhite(arg));
+               if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
+                       && (checkforcmd(&p, "let", 2)
+                           || checkforcmd(&p, "var", 3)
+                           || checkforcmd(&p, "final", 5)
+                           || checkforcmd(&p, "const", 5))))
+               {
+                   p = skipwhite(arg + 3);
+                   if (STRNCMP(p, "trim", 4) == 0)
+                   {
+                       // Ignore leading white space.
+                       p = skipwhite(p + 4);
+                       heredoc_trimmed = vim_strnsave(theline,
+                                                skipwhite(theline) - theline);
+                   }
+                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
+                   getline_options = GETLINE_NONE;
+                   is_heredoc = TRUE;
+               }
+           }
+       }
+
+       // Add the line to the function.
+       if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
+           goto theend;
+
+       // Copy the line to newly allocated memory.  get_one_sourceline()
+       // allocates 250 bytes per line, this saves 80% on average.  The cost
+       // is an extra alloc/free.
+       p = vim_strsave(theline);
+       if (p == NULL)
+           goto theend;
+       ((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
+
+       // Add NULL lines for continuation lines, so that the line count is
+       // equal to the index in the growarray.
+       while (sourcing_lnum_off-- > 0)
+           ((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
+
+       // Check for end of eap->arg.
+       if (line_arg != NULL && *line_arg == NUL)
+           line_arg = NULL;
+    }
+
+    // Don't define the function when skipping commands or when an error was
+    // detected.
+    if (!eap->skip && !did_emsg)
+       ret = OK;
+
+theend:
+    vim_free(skip_until);
+    vim_free(heredoc_trimmed);
+    need_wait_return |= saved_wait_return;
+    return ret;
+}
+
+/*
+ * Handle the body of a lambda.  *arg points to the "{", process statements
+ * until the matching "}".
+ * When successful "rettv" is set to a funcref.
+ */
+    static int
+lambda_function_body(
+       char_u      **arg,
+       typval_T    *rettv,
+       evalarg_T   *evalarg,
+       garray_T    *newargs,
+       garray_T    *argtypes,
+       int         varargs,
+       garray_T    *default_args,
+       char_u      *ret_type)
+{
+    int                evaluate = evalarg != NULL
+                                     && (evalarg->eval_flags & EVAL_EVALUATE);
+    ufunc_T    *ufunc;
+    exarg_T    eap;
+    garray_T   newlines;
+    char_u     *cmdline = NULL;
+    int                ret = FAIL;
+    char_u     *line_to_free = NULL;
+    partial_T  *pt;
+    char_u     *name;
+    int                lnum_save = -1;
+    linenr_T   sourcing_lnum_top = SOURCING_LNUM;
+
+    CLEAR_FIELD(eap);
+    eap.cmdidx = CMD_block;
+    eap.forceit = FALSE;
+    eap.arg = *arg + 1;
+    eap.cmdlinep = &cmdline;
+    eap.skip = !evaluate;
+    if (evalarg->eval_cctx != NULL)
+       fill_exarg_from_cctx(&eap, evalarg->eval_cctx);
+    else
+    {
+       eap.getline = evalarg->eval_getline;
+       eap.cookie = evalarg->eval_cookie;
+    }
+
+    ga_init2(&newlines, (int)sizeof(char_u *), 10);
+    if (get_function_body(&eap, &newlines, NULL, &line_to_free) == FAIL)
+       goto erret;
+    if (cmdline != NULL)
+    {
+       // Something comes after the "}".
+       *arg = eap.nextcmd;
+       if (evalarg->eval_cctx == NULL)
+       {
+           // Need to keep the line and free it/ later.
+           vim_free(evalarg->eval_tofree_lambda);
+           evalarg->eval_tofree_lambda = cmdline;
+       }
     }
-    if (*s != '>')
-       return NULL;
-    if (white_error != NULL && ((!VIM_ISWHITE(*bef) && *bef != '{')
-               || !IS_WHITE_OR_NUL(s[1])))
+    else
+       *arg = (char_u *)"";
+
+    name = get_lambda_name();
+    ufunc = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+    if (ufunc == NULL)
+       goto erret;
+    set_ufunc_name(ufunc, name);
+    if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
     {
-       *white_error = TRUE;
-       semsg(_(e_white_space_required_before_and_after_str_at_str),
-                                              equal_arrow ? "=>" : "->", bef);
-       return NULL;
+       vim_free(ufunc);
+       goto erret;
     }
-    return skipwhite(s + 1);
+    ufunc->uf_refcount = 1;
+    ufunc->uf_args = *newargs;
+    newargs->ga_data = NULL;
+    ufunc->uf_def_args = *default_args;
+    default_args->ga_data = NULL;
+    ufunc->uf_func_type = &t_func_any;
+
+    // error messages are for the first function line
+    lnum_save = SOURCING_LNUM;
+    SOURCING_LNUM = sourcing_lnum_top;
+
+    // parse argument types
+    if (parse_argument_types(ufunc, argtypes, varargs) == FAIL)
+    {
+       SOURCING_LNUM = lnum_save;
+       goto erret;
+    }
+
+    // parse the return type, if any
+    if (parse_return_type(ufunc, ret_type) == FAIL)
+       goto erret;
+
+    pt = ALLOC_CLEAR_ONE(partial_T);
+    if (pt == NULL)
+       goto erret;
+    pt->pt_func = ufunc;
+    pt->pt_refcount = 1;
+
+    ufunc->uf_lines = newlines;
+    newlines.ga_data = NULL;
+    if (sandbox)
+       ufunc->uf_flags |= FC_SANDBOX;
+    if (!ASCII_ISUPPER(*ufunc->uf_name))
+       ufunc->uf_flags |= FC_VIM9;
+    ufunc->uf_script_ctx = current_sctx;
+    ufunc->uf_script_ctx_version = current_sctx.sc_version;
+    ufunc->uf_script_ctx.sc_lnum += sourcing_lnum_top;
+    set_function_type(ufunc);
+
+    rettv->vval.v_partial = pt;
+    rettv->v_type = VAR_PARTIAL;
+    ret = OK;
+
+erret:
+    if (lnum_save >= 0)
+       SOURCING_LNUM = lnum_save;
+    vim_free(line_to_free);
+    ga_clear_strings(&newlines);
+    ga_clear_strings(newargs);
+    ga_clear_strings(default_args);
+    return ret;
 }
 
 /*
- * Parse a lambda expression and get a Funcref from "*arg".
+ * Parse a lambda expression and get a Funcref from "*arg" into "rettv".
  * "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
  * When "types_optional" is TRUE optionally take argument types.
  * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
@@ -554,6 +1067,7 @@ get_lambda_tv(
     garray_T   newlines;
     garray_T   *pnewargs;
     garray_T   argtypes;
+    garray_T   default_args;
     ufunc_T    *fp = NULL;
     partial_T   *pt = NULL;
     int                varargs;
@@ -596,7 +1110,8 @@ get_lambda_tv(
     *arg += 1;
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
-                                           &varargs, NULL, FALSE, NULL, NULL);
+                                           &varargs, &default_args,
+                                           FALSE, NULL, NULL);
     if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
                equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
@@ -624,9 +1139,15 @@ get_lambda_tv(
     // Recognize "{" as the start of a function body.
     if (equal_arrow && **arg == '{')
     {
-       // TODO: process the function body upto the "}".
-       // Return type is required then.
-       emsg("Lambda function body not supported yet");
+       if (lambda_function_body(arg, rettv, evalarg, pnewargs,
+                          types_optional ? &argtypes : NULL, varargs,
+                          &default_args, ret_type) == FAIL)
+           goto errret;
+       goto theend;
+    }
+    if (default_args.ga_len > 0)
+    {
+       emsg(_(e_cannot_use_default_values_in_lambda));
        goto errret;
     }
 
@@ -732,6 +1253,7 @@ get_lambda_tv(
        hash_add(&func_hashtab, UF2HIKEY(fp));
     }
 
+theend:
     eval_lavars_used = old_eval_lavars;
     if (evalarg != NULL && evalarg->eval_tofree == NULL)
        evalarg->eval_tofree = tofree1;
@@ -745,6 +1267,7 @@ get_lambda_tv(
 errret:
     ga_clear_strings(&newargs);
     ga_clear_strings(&newlines);
+    ga_clear_strings(&default_args);
     if (types_optional)
        ga_clear_strings(&argtypes);
     vim_free(fp);
@@ -2459,7 +2982,7 @@ call_func(
     {
        // Check that the argument types are OK for the types of the funcref.
        if (check_argument_types(funcexe->check_type, argvars, argcount,
-                                                                name) == FAIL)
+                                    (name != NULL) ? name : funcname) == FAIL)
            error = FCERR_OTHER;
     }
 
@@ -3005,27 +3528,6 @@ list_functions(regmatch_T *regmatch)
     }
 }
 
-/*
- * Check if "*cmd" points to a function command and if so advance "*cmd" and
- * return TRUE.
- * Otherwise return FALSE;
- * Do not consider "function(" to be a command.
- */
-    static int
-is_function_cmd(char_u **cmd)
-{
-    char_u *p = *cmd;
-
-    if (checkforcmd(&p, "function", 2))
-    {
-       if (*p == '(')
-           return FALSE;
-       *cmd = p;
-       return TRUE;
-    }
-    return FALSE;
-}
-
 /*
  * ":function" also supporting nested ":def".
  * When "name_arg" is not NULL this is a nested function, using "name_arg" for
@@ -3035,12 +3537,10 @@ is_function_cmd(char_u **cmd)
     ufunc_T *
 define_function(exarg_T *eap, char_u *name_arg)
 {
-    char_u     *theline;
     char_u     *line_to_free = NULL;
     int                j;
     int                c;
     int                saved_did_emsg;
-    int                saved_wait_return = need_wait_return;
     char_u     *name = name_arg;
     int                is_global = FALSE;
     char_u     *p;
@@ -3056,21 +3556,12 @@ define_function(exarg_T *eap, char_u *name_arg)
     char_u     *ret_type = NULL;
     ufunc_T    *fp = NULL;
     int                overwrite = FALSE;
-    int                indent;
-    int                nesting;
-#define MAX_FUNC_NESTING 50
-    char       nesting_def[MAX_FUNC_NESTING];
     dictitem_T *v;
     funcdict_T fudi;
     static int func_nr = 0;        // number for nameless function
     int                paren;
     hashitem_T *hi;
-    getline_opt_T getline_options;
-    linenr_T   sourcing_lnum_off;
     linenr_T   sourcing_lnum_top;
-    int                is_heredoc = FALSE;
-    char_u     *skip_until = NULL;
-    char_u     *heredoc_trimmed = NULL;
     int                vim9script = in_vim9script();
     imported_T *import = NULL;
 
@@ -3263,7 +3754,7 @@ define_function(exarg_T *eap, char_u *name_arg)
        goto ret_free;
     }
 
-    ga_init2(&newlines, (int)sizeof(char_u *), 3);
+    ga_init2(&newlines, (int)sizeof(char_u *), 10);
 
     if (!eap->skip && name_arg == NULL)
     {
@@ -3305,403 +3796,101 @@ define_function(exarg_T *eap, char_u *name_arg)
     if (eap->cmdidx == CMD_def)
     {
        // find the return type: :def Func(): type
-       if (*p == ':')
-       {
-           ret_type = skipwhite(p + 1);
-           p = skip_type(ret_type, FALSE);
-           if (p > ret_type)
-           {
-               ret_type = vim_strnsave(ret_type, p - ret_type);
-               whitep = p;
-               p = skipwhite(p);
-           }
-           else
-           {
-               semsg(_(e_expected_type_str), ret_type);
-               ret_type = NULL;
-           }
-       }
-       p = skipwhite(p);
-    }
-    else
-       // find extra arguments "range", "dict", "abort" and "closure"
-       for (;;)
-       {
-           whitep = p;
-           p = skipwhite(p);
-           if (STRNCMP(p, "range", 5) == 0)
-           {
-               flags |= FC_RANGE;
-               p += 5;
-           }
-           else if (STRNCMP(p, "dict", 4) == 0)
-           {
-               flags |= FC_DICT;
-               p += 4;
-           }
-           else if (STRNCMP(p, "abort", 5) == 0)
-           {
-               flags |= FC_ABORT;
-               p += 5;
-           }
-           else if (STRNCMP(p, "closure", 7) == 0)
-           {
-               flags |= FC_CLOSURE;
-               p += 7;
-               if (current_funccal == NULL)
-               {
-                   emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
-                           name == NULL ? (char_u *)"" : name);
-                   goto erret;
-               }
-           }
-           else
-               break;
-       }
-
-    // When there is a line break use what follows for the function body.
-    // Makes 'exe "func Test()\n...\nendfunc"' work.
-    if (*p == '\n')
-       line_arg = p + 1;
-    else if (*p != NUL
-           && !(*p == '"' && (!vim9script || eap->cmdidx == CMD_function)
-                                                    && eap->cmdidx != CMD_def)
-           && !(VIM_ISWHITE(*whitep) && *p == '#'
-                                    && (vim9script || eap->cmdidx == CMD_def))
-           && !eap->skip
-           && !did_emsg)
-       semsg(_(e_trailing_arg), p);
-
-    /*
-     * Read the body of the function, until "}", ":endfunction" or ":enddef" is
-     * found.
-     */
-    if (KeyTyped)
-    {
-       // Check if the function already exists, don't let the user type the
-       // whole function before telling him it doesn't work!  For a script we
-       // need to skip the body to be able to find what follows.
-       if (!eap->skip && !eap->forceit)
-       {
-           if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
-               emsg(_(e_funcdict));
-           else if (name != NULL && find_func(name, is_global, NULL) != NULL)
-               emsg_funcname(e_funcexts, name);
-       }
-
-       if (!eap->skip && did_emsg)
-           goto erret;
-
-       msg_putchar('\n');          // don't overwrite the function name
-       cmdline_row = msg_row;
-    }
-
-    // Save the starting line number.
-    sourcing_lnum_top = SOURCING_LNUM;
-
-    // Detect having skipped over comment lines to find the return
-    // type.  Add NULL lines to keep the line count correct.
-    sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
-    if (SOURCING_LNUM < sourcing_lnum_off)
-    {
-       sourcing_lnum_off -= SOURCING_LNUM;
-       if (ga_grow(&newlines, sourcing_lnum_off) == FAIL)
-           goto erret;
-       while (sourcing_lnum_off-- > 0)
-           ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
-    }
-
-    indent = 2;
-    nesting = 0;
-    nesting_def[nesting] = (eap->cmdidx == CMD_def);
-    getline_options = eap->cmdidx == CMD_def
-                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
-    for (;;)
-    {
-       if (KeyTyped)
-       {
-           msg_scroll = TRUE;
-           saved_wait_return = FALSE;
-       }
-       need_wait_return = FALSE;
-
-       if (line_arg != NULL)
-       {
-           // Use eap->arg, split up in parts by line breaks.
-           theline = line_arg;
-           p = vim_strchr(theline, '\n');
-           if (p == NULL)
-               line_arg += STRLEN(line_arg);
-           else
-           {
-               *p = NUL;
-               line_arg = p + 1;
-           }
-       }
-       else
-       {
-           vim_free(line_to_free);
-           if (eap->getline == NULL)
-               theline = getcmdline(':', 0L, indent, getline_options);
-           else
-               theline = eap->getline(':', eap->cookie, indent,
-                                                             getline_options);
-           line_to_free = theline;
-       }
-       if (KeyTyped)
-           lines_left = Rows - 1;
-       if (theline == NULL)
-       {
-           // Use the start of the function for the line number.
-           SOURCING_LNUM = sourcing_lnum_top;
-           if (skip_until != NULL)
-               semsg(_(e_missing_heredoc_end_marker_str), skip_until);
-           else if (eap->cmdidx == CMD_def)
-               emsg(_(e_missing_enddef));
-           else
-               emsg(_("E126: Missing :endfunction"));
-           goto erret;
-       }
-
-       // Detect line continuation: SOURCING_LNUM increased more than one.
-       sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
-       if (SOURCING_LNUM < sourcing_lnum_off)
-           sourcing_lnum_off -= SOURCING_LNUM;
-       else
-           sourcing_lnum_off = 0;
-
-       if (skip_until != NULL)
-       {
-           // Don't check for ":endfunc"/":enddef" between
-           // * ":append" and "."
-           // * ":python <<EOF" and "EOF"
-           // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
-           if (heredoc_trimmed == NULL
-                   || (is_heredoc && skipwhite(theline) == theline)
-                   || STRNCMP(theline, heredoc_trimmed,
-                                                STRLEN(heredoc_trimmed)) == 0)
-           {
-               if (heredoc_trimmed == NULL)
-                   p = theline;
-               else if (is_heredoc)
-                   p = skipwhite(theline) == theline
-                                ? theline : theline + STRLEN(heredoc_trimmed);
-               else
-                   p = theline + STRLEN(heredoc_trimmed);
-               if (STRCMP(p, skip_until) == 0)
-               {
-                   VIM_CLEAR(skip_until);
-                   VIM_CLEAR(heredoc_trimmed);
-                   getline_options = eap->cmdidx == CMD_def
-                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
-                   is_heredoc = FALSE;
-               }
+       if (*p == ':')
+       {
+           ret_type = skipwhite(p + 1);
+           p = skip_type(ret_type, FALSE);
+           if (p > ret_type)
+           {
+               ret_type = vim_strnsave(ret_type, p - ret_type);
+               whitep = p;
+               p = skipwhite(p);
+           }
+           else
+           {
+               semsg(_(e_expected_type_str), ret_type);
+               ret_type = NULL;
            }
        }
-       else
+       p = skipwhite(p);
+    }
+    else
+       // find extra arguments "range", "dict", "abort" and "closure"
+       for (;;)
        {
-           // skip ':' and blanks
-           for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
-               ;
-
-           // Check for "endfunction" or "enddef".
-           // When a ":" follows it must be a dict key; "enddef: value,"
-           if (checkforcmd(&p, nesting_def[nesting]
-                                               ? "enddef" : "endfunction", 4)
-                   && *p != ':')
+           whitep = p;
+           p = skipwhite(p);
+           if (STRNCMP(p, "range", 5) == 0)
            {
-               if (nesting-- == 0)
-               {
-                   char_u *nextcmd = NULL;
-
-                   if (*p == '|')
-                       nextcmd = p + 1;
-                   else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
-                       nextcmd = line_arg;
-                   else if (*p != NUL && *p != '"' && p_verbose > 0)
-                       give_warning2(eap->cmdidx == CMD_def
-                           ? (char_u *)_("W1001: Text found after :enddef: %s")
-                           : (char_u *)_("W22: Text found after :endfunction: %s"),
-                            p, TRUE);
-                   if (nextcmd != NULL)
-                   {
-                       // Another command follows. If the line came from "eap"
-                       // we can simply point into it, otherwise we need to
-                       // change "eap->cmdlinep".
-                       eap->nextcmd = nextcmd;
-                       if (line_to_free != NULL)
-                       {
-                           vim_free(*eap->cmdlinep);
-                           *eap->cmdlinep = line_to_free;
-                           line_to_free = NULL;
-                       }
-                   }
-                   break;
-               }
+               flags |= FC_RANGE;
+               p += 5;
            }
-
-           // Check for mismatched "endfunc" or "enddef".
-           // We don't check for "def" inside "func" thus we also can't check
-           // for "enddef".
-           // We continue to find the end of the function, although we might
-           // not find it.
-           else if (nesting_def[nesting])
+           else if (STRNCMP(p, "dict", 4) == 0)
            {
-               if (checkforcmd(&p, "endfunction", 4) && *p != ':')
-                   emsg(_(e_mismatched_endfunction));
+               flags |= FC_DICT;
+               p += 4;
            }
-           else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
-               emsg(_(e_mismatched_enddef));
-
-           // Increase indent inside "if", "while", "for" and "try", decrease
-           // at "end".
-           if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
-               indent -= 2;
-           else if (STRNCMP(p, "if", 2) == 0
-                   || STRNCMP(p, "wh", 2) == 0
-                   || STRNCMP(p, "for", 3) == 0
-                   || STRNCMP(p, "try", 3) == 0)
-               indent += 2;
-
-           // Check for defining a function inside this function.
-           // Only recognize "def" inside "def", not inside "function",
-           // For backwards compatibility, see Test_function_python().
-           c = *p;
-           if (is_function_cmd(&p)
-                   || (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
+           else if (STRNCMP(p, "abort", 5) == 0)
            {
-               if (*p == '!')
-                   p = skipwhite(p + 1);
-               p += eval_fname_script(p);
-               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
-                                                                 NULL, NULL));
-               if (*skipwhite(p) == '(')
-               {
-                   if (nesting == MAX_FUNC_NESTING - 1)
-                       emsg(_(e_function_nesting_too_deep));
-                   else
-                   {
-                       ++nesting;
-                       nesting_def[nesting] = (c == 'd');
-                       indent += 2;
-                   }
-               }
+               flags |= FC_ABORT;
+               p += 5;
            }
-
-           // Check for ":append", ":change", ":insert".  Not for :def.
-           p = skip_range(p, FALSE, NULL);
-           if (eap->cmdidx != CMD_def
-               && ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
-                   || (p[0] == 'c'
-                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
-                               && (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
-                                       && (STRNCMP(&p[3], "nge", 3) != 0
-                                           || !ASCII_ISALPHA(p[6])))))))
-                   || (p[0] == 'i'
-                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
-                               && (!ASCII_ISALPHA(p[2])
-                                   || (p[2] == 's'
-                                       && (!ASCII_ISALPHA(p[3])
-                                               || p[3] == 'e'))))))))
-               skip_until = vim_strsave((char_u *)".");
-
-           // Check for ":python <<EOF", ":tcl <<EOF", etc.
-           arg = skipwhite(skiptowhite(p));
-           if (arg[0] == '<' && arg[1] =='<'
-                   && ((p[0] == 'p' && p[1] == 'y'
-                                   && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
-                                       || ((p[2] == '3' || p[2] == 'x')
-                                                  && !ASCII_ISALPHA(p[3]))))
-                       || (p[0] == 'p' && p[1] == 'e'
-                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
-                       || (p[0] == 't' && p[1] == 'c'
-                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
-                       || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
-                                   && !ASCII_ISALPHA(p[3]))
-                       || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
-                                   && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
-                       || (p[0] == 'm' && p[1] == 'z'
-                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
-                       ))
+           else if (STRNCMP(p, "closure", 7) == 0)
            {
-               // ":python <<" continues until a dot, like ":append"
-               p = skipwhite(arg + 2);
-               if (STRNCMP(p, "trim", 4) == 0)
+               flags |= FC_CLOSURE;
+               p += 7;
+               if (current_funccal == NULL)
                {
-                   // Ignore leading white space.
-                   p = skipwhite(p + 4);
-                   heredoc_trimmed = vim_strnsave(theline,
-                                                skipwhite(theline) - theline);
+                   emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
+                           name == NULL ? (char_u *)"" : name);
+                   goto erret;
                }
-               if (*p == NUL)
-                   skip_until = vim_strsave((char_u *)".");
-               else
-                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
-               getline_options = GETLINE_NONE;
-               is_heredoc = TRUE;
            }
+           else
+               break;
+       }
 
-           // Check for ":cmd v =<< [trim] EOF"
-           //       and ":cmd [a, b] =<< [trim] EOF"
-           //       and "lines =<< [trim] EOF" for Vim9
-           // Where "cmd" can be "let", "var", "final" or "const".
-           arg = skipwhite(skiptowhite(p));
-           if (*arg == '[')
-               arg = vim_strchr(arg, ']');
-           if (arg != NULL)
-           {
-               int found = (eap->cmdidx == CMD_def && arg[0] == '='
-                                            && arg[1] == '<' && arg[2] =='<');
+    // When there is a line break use what follows for the function body.
+    // Makes 'exe "func Test()\n...\nendfunc"' work.
+    if (*p == '\n')
+       line_arg = p + 1;
+    else if (*p != NUL
+           && !(*p == '"' && (!vim9script || eap->cmdidx == CMD_function)
+                                                    && eap->cmdidx != CMD_def)
+           && !(VIM_ISWHITE(*whitep) && *p == '#'
+                                    && (vim9script || eap->cmdidx == CMD_def))
+           && !eap->skip
+           && !did_emsg)
+       semsg(_(e_trailing_arg), p);
 
-               if (!found)
-                   // skip over the argument after "cmd"
-                   arg = skipwhite(skiptowhite(arg));
-               if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
-                       && (checkforcmd(&p, "let", 2)
-                           || checkforcmd(&p, "var", 3)
-                           || checkforcmd(&p, "final", 5)
-                           || checkforcmd(&p, "const", 5))))
-               {
-                   p = skipwhite(arg + 3);
-                   if (STRNCMP(p, "trim", 4) == 0)
-                   {
-                       // Ignore leading white space.
-                       p = skipwhite(p + 4);
-                       heredoc_trimmed = vim_strnsave(theline,
-                                                skipwhite(theline) - theline);
-                   }
-                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
-                   getline_options = GETLINE_NONE;
-                   is_heredoc = TRUE;
-               }
-           }
+    /*
+     * Read the body of the function, until "}", ":endfunction" or ":enddef" is
+     * found.
+     */
+    if (KeyTyped)
+    {
+       // Check if the function already exists, don't let the user type the
+       // whole function before telling him it doesn't work!  For a script we
+       // need to skip the body to be able to find what follows.
+       if (!eap->skip && !eap->forceit)
+       {
+           if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
+               emsg(_(e_funcdict));
+           else if (name != NULL && find_func(name, is_global, NULL) != NULL)
+               emsg_funcname(e_funcexts, name);
        }
 
-       // Add the line to the function.
-       if (ga_grow(&newlines, 1 + sourcing_lnum_off) == FAIL)
-           goto erret;
-
-       // Copy the line to newly allocated memory.  get_one_sourceline()
-       // allocates 250 bytes per line, this saves 80% on average.  The cost
-       // is an extra alloc/free.
-       p = vim_strsave(theline);
-       if (p == NULL)
+       if (!eap->skip && did_emsg)
            goto erret;
-       ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
 
-       // Add NULL lines for continuation lines, so that the line count is
-       // equal to the index in the growarray.
-       while (sourcing_lnum_off-- > 0)
-           ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
-
-       // Check for end of eap->arg.
-       if (line_arg != NULL && *line_arg == NUL)
-           line_arg = NULL;
+       msg_putchar('\n');          // don't overwrite the function name
+       cmdline_row = msg_row;
     }
 
-    // Don't define the function when skipping commands or when an error was
-    // detected.
-    if (eap->skip || did_emsg)
+    // Save the starting line number.
+    sourcing_lnum_top = SOURCING_LNUM;
+
+    if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL)
        goto erret;
 
     /*
@@ -3933,18 +4122,10 @@ define_function(exarg_T *eap, char_u *name_arg)
        varargs = FALSE;
 
        // parse the return type, if any
-       if (ret_type == NULL)
-           fp->uf_ret_type = &t_void;
-       else
+       if (parse_return_type(fp, ret_type) == FAIL)
        {
-           p = ret_type;
-           fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
-           if (fp->uf_ret_type == NULL)
-           {
-               fp->uf_ret_type = &t_void;
-               SOURCING_LNUM = lnum_save;
-               goto erret;
-           }
+           SOURCING_LNUM = lnum_save;
+           goto erret;
        }
        SOURCING_LNUM = lnum_save;
     }
@@ -4004,15 +4185,12 @@ errret_2:
        VIM_CLEAR(fp->uf_arg_types);
 ret_free:
     ga_clear_strings(&argtypes);
-    vim_free(skip_until);
-    vim_free(heredoc_trimmed);
     vim_free(line_to_free);
     vim_free(fudi.fd_newkey);
     if (name != name_arg)
        vim_free(name);
     vim_free(ret_type);
     did_emsg |= saved_did_emsg;
-    need_wait_return |= saved_wait_return;
 
     return fp;
 }
index 2ed5acaf23c33a8aae2ad81ffbb7902dd0fa9bbe..cdce8cc767d91de845a475fc6dcb6366b298fc22 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2635,
 /**/
     2634,
 /**/
index 238ce5d1fac14bf1319c6d7422238ad55f2c8a71..09b1fce83baea3ebcece3c359eec3d3aac0b931a 100644 (file)
@@ -3171,7 +3171,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
 
 /*
  * Parse a lambda: "(arg, arg) => expr"
- * "*arg" points to the '{'.
+ * "*arg" points to the '('.
  * Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
  */
     static int
@@ -5126,6 +5126,13 @@ exarg_getline(
     }
 }
 
+    void
+fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx)
+{
+    eap->getline = exarg_getline;
+    eap->cookie = cctx;
+}
+
 /*
  * Compile a nested :def command.
  */
@@ -5176,9 +5183,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
        return NULL;
 
     eap->arg = name_end;
-    eap->getline = exarg_getline;
-    eap->cookie = cctx;
-    eap->skip = cctx->ctx_skip == SKIP_YES;
+    fill_exarg_from_cctx(eap, cctx);
+
     eap->forceit = FALSE;
     lambda_name = vim_strsave(get_lambda_name());
     if (lambda_name == NULL)