]> granicus.if.org Git - vim/commitdiff
patch 8.1.1820: using expr->FuncRef() does not work v8.1.1820
authorBram Moolenaar <Bram@vim.org>
Mon, 5 Aug 2019 21:10:16 +0000 (23:10 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 5 Aug 2019 21:10:16 +0000 (23:10 +0200)
Problem:    Using expr->FuncRef() does not work.
Solution:   Make FuncRef work as a method.

src/eval.c
src/testdir/test_method.vim
src/userfunc.c
src/version.c

index 16ced406034e61d69b3c176246b656b34ec12f47..caac2a1da2c1fae6c0bdc7557ff83216fcf9ef6c 100644 (file)
@@ -3654,6 +3654,74 @@ pattern_match(char_u *pat, char_u *text, int ic)
     return matches;
 }
 
+/*
+ * Handle a name followed by "(".  Both for just "name(arg)" and for
+ * "expr->name(arg)".
+ * Returns OK or FAIL.
+ */
+    static int
+eval_func(
+       char_u      **arg,      // points to "(", will be advanced
+       char_u      *name,
+       int         name_len,
+       typval_T    *rettv,
+       int         evaluate,
+       typval_T    *basetv)    // "expr" for "expr->name(arg)"
+{
+    char_u     *s = name;
+    int                len = name_len;
+    partial_T  *partial;
+    int                ret = OK;
+
+    if (!evaluate)
+       check_vars(s, len);
+
+    /* If "s" is the name of a variable of type VAR_FUNC
+     * use its contents. */
+    s = deref_func_name(s, &len, &partial, !evaluate);
+
+    /* Need to make a copy, in case evaluating the arguments makes
+     * the name invalid. */
+    s = vim_strsave(s);
+    if (s == NULL)
+       ret = FAIL;
+    else
+    {
+       funcexe_T funcexe;
+
+       // Invoke the function.
+       vim_memset(&funcexe, 0, sizeof(funcexe));
+       funcexe.firstline = curwin->w_cursor.lnum;
+       funcexe.lastline = curwin->w_cursor.lnum;
+       funcexe.doesrange = &len;
+       funcexe.evaluate = evaluate;
+       funcexe.partial = partial;
+       funcexe.basetv = basetv;
+       ret = get_func_tv(s, len, rettv, arg, &funcexe);
+    }
+    vim_free(s);
+
+    /* If evaluate is FALSE rettv->v_type was not set in
+     * get_func_tv, but it's needed in handle_subscript() to parse
+     * what follows. So set it here. */
+    if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(')
+    {
+       rettv->vval.v_string = NULL;
+       rettv->v_type = VAR_FUNC;
+    }
+
+    /* Stop the expression evaluation when immediately
+     * aborting on error, or when an interrupt occurred or
+     * an exception was thrown but not caught. */
+    if (evaluate && aborting())
+    {
+       if (ret == OK)
+           clear_tv(rettv);
+       ret = FAIL;
+    }
+    return ret;
+}
+
 /*
  * The "evaluate" argument: When FALSE, the argument is only parsed but not
  * executed.  The function may return OK, but the rettv will be of type
@@ -4671,55 +4739,7 @@ eval7(
        else
        {
            if (**arg == '(')           /* recursive! */
-           {
-               partial_T *partial;
-
-               if (!evaluate)
-                   check_vars(s, len);
-
-               /* If "s" is the name of a variable of type VAR_FUNC
-                * use its contents. */
-               s = deref_func_name(s, &len, &partial, !evaluate);
-
-               /* Need to make a copy, in case evaluating the arguments makes
-                * the name invalid. */
-               s = vim_strsave(s);
-               if (s == NULL)
-                   ret = FAIL;
-               else
-               {
-                   funcexe_T funcexe;
-
-                   // Invoke the function.
-                   vim_memset(&funcexe, 0, sizeof(funcexe));
-                   funcexe.firstline = curwin->w_cursor.lnum;
-                   funcexe.lastline = curwin->w_cursor.lnum;
-                   funcexe.doesrange = &len;
-                   funcexe.evaluate = evaluate;
-                   funcexe.partial = partial;
-                   ret = get_func_tv(s, len, rettv, arg, &funcexe);
-               }
-               vim_free(s);
-
-               /* If evaluate is FALSE rettv->v_type was not set in
-                * get_func_tv, but it's needed in handle_subscript() to parse
-                * what follows. So set it here. */
-               if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(')
-               {
-                   rettv->vval.v_string = NULL;
-                   rettv->v_type = VAR_FUNC;
-               }
-
-               /* Stop the expression evaluation when immediately
-                * aborting on error, or when an interrupt occurred or
-                * an exception was thrown but not caught. */
-               if (evaluate && aborting())
-               {
-                   if (ret == OK)
-                       clear_tv(rettv);
-                   ret = FAIL;
-               }
-           }
+               ret = eval_func(arg, s, len, rettv, evaluate, NULL);
            else if (evaluate)
                ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
            else
@@ -4815,55 +4835,42 @@ eval_method(
 {
     char_u     *name;
     long       len;
-    funcexe_T  funcexe;
-    int                ret = OK;
+    char_u     *alias;
     typval_T   base = *rettv;
+    int                ret;
 
     // Skip over the ->.
     *arg += 2;
+    rettv->v_type = VAR_UNKNOWN;
 
-    // Locate the method name.
     name = *arg;
-    for (len = 0; eval_isnamec(name[len]); ++len)
-       ;
-    if (len == 0)
+    len = get_name_len(arg, &alias, evaluate, TRUE);
+    if (alias != NULL)
+       name = alias;
+
+    if (len <= 0)
     {
        if (verbose)
            emsg(_("E260: Missing name after ->"));
-       return FAIL;
+       ret = FAIL;
     }
-
-    // Check for the "(".  Skip over white space after it.
-    if (name[len] != '(')
+    else
     {
-       if (verbose)
-           semsg(_(e_missingparen), name);
-       return FAIL;
+       if (**arg != '(')
+       {
+           if (verbose)
+               semsg(_(e_missingparen), name);
+           ret = FAIL;
+       }
+       else
+           ret = eval_func(arg, name, len, rettv, evaluate, &base);
     }
-    *arg += len;
-
-    // TODO: if "name" is a function reference, resolve it.
-
-    vim_memset(&funcexe, 0, sizeof(funcexe));
-    funcexe.evaluate = evaluate;
-    funcexe.basetv = &base;
-    rettv->v_type = VAR_UNKNOWN;
-    ret = get_func_tv(name, len, rettv, arg, &funcexe);
 
     /* Clear the funcref afterwards, so that deleting it while
      * evaluating the arguments is possible (see test55). */
     if (evaluate)
        clear_tv(&base);
 
-    /* Stop the expression evaluation when immediately aborting on
-     * error, or when an interrupt occurred or an exception was thrown
-     * but not caught. */
-    if (aborting())
-    {
-       if (ret == OK)
-           clear_tv(rettv);
-       ret = FAIL;
-    }
     return ret;
 }
 
index 93918c83bfb9bf1de3b39a45ec6128c34afe18a8..6d90a8f82188890efadaa2a45e881035d9f2413b 100644 (file)
@@ -1,6 +1,6 @@
 " Tests for ->method()
 
-func Test_list()
+func Test_list_method()
   let l = [1, 2, 3]
   call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4))
   eval l->assert_equal(l)
@@ -34,7 +34,7 @@ func Test_list()
   call assert_fails('eval l->values()', 'E715:')
 endfunc
 
-func Test_dict()
+func Test_dict_method()
   let d = #{one: 1, two: 2, three: 3}
 
   call assert_equal(d, d->copy())
@@ -66,7 +66,7 @@ func Test_dict()
   call assert_equal([1, 2, 3], d->values())
 endfunc
 
-func Test_string()
+func Test_string_method()
   call assert_equal(['1', '2', '3'], '1 2 3'->split())
   call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)}))
   call assert_equal([65, 66, 67], 'ABC'->str2list())
@@ -76,7 +76,7 @@ func Test_string()
   call assert_equal('axc', 'abc'->substitute('b', 'x', ''))
 endfunc
 
-func Test_append()
+func Test_method_append()
   new
   eval ['one', 'two', 'three']->append(1)
   call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
@@ -89,3 +89,16 @@ func Test_append()
 
   exe 'bwipe! ' .. bnr
 endfunc
+
+func Test_method_funcref()
+  func Concat(one, two, three)
+    return a:one .. a:two .. a:three
+  endfunc
+  let FuncRef = function('Concat')
+  eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail')
+
+  let Partial = function('Concat', ['two'])
+  eval 'one'->Partial('three')->assert_equal('onetwothree')
+
+  delfunc Concat
+endfunc
index a609196185b9c59a8d8ba69e9509825c76a0de3c..a739a757b7213f3b5cd9b2a349e356250eefd068 100644 (file)
@@ -1498,6 +1498,7 @@ call_func(
     typval_T   argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
                                         // "funcexe->basetv" is not NULL
     int                argv_clear = 0;
+    int                argv_base = 0;
     partial_T  *partial = funcexe->partial;
 
     // Make a copy of the name, if it comes from a funcref variable it could
@@ -1590,9 +1591,9 @@ call_func(
                    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount);
                    argv[0] = *funcexe->basetv;
                    argcount++;
+                   argvars = argv;
+                   argv_base = 1;
                }
-               else
-                   memcpy(argv, argvars, sizeof(typval_T) * argcount);
 
                if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
                    *funcexe->doesrange = TRUE;
@@ -1621,7 +1622,7 @@ call_func(
                        did_save_redo = TRUE;
                    }
                    ++fp->uf_calls;
-                   call_user_func(fp, argcount, argv, rettv,
+                   call_user_func(fp, argcount, argvars, rettv,
                                         funcexe->firstline, funcexe->lastline,
                                  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
                    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
@@ -1698,8 +1699,10 @@ call_func(
        }
     }
 
+    // clear the copies made from the partial
     while (argv_clear > 0)
-       clear_tv(&argv[--argv_clear]);
+       clear_tv(&argv[--argv_clear + argv_base]);
+
     vim_free(tofree);
     vim_free(name);
 
index 76bb414cc41c99791760fe56c5b366c85b65674c..b0d9481a6251e211cd2d7ec0ddeb02120fb85eec 100644 (file)
@@ -773,6 +773,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1820,
 /**/
     1819,
 /**/