changeset 8583:537bbbd4e987 v7.4.1581

commit https://github.com/vim/vim/commit/65639032bb7b17996cd255d1508a1df4ad528a1f Author: Bram Moolenaar <Bram@vim.org> Date: Wed Mar 16 21:40:30 2016 +0100 patch 7.4.1581 Problem: Using ":call dict.func()" where the function is a partial does not work. Using "dict.func()" where the function does not take a Dictionary does not work. Solution: Handle partial properly in ":call". (Yasuhiro Matsumoto)
author Christian Brabandt <cb@256bit.org>
date Wed, 16 Mar 2016 21:45:04 +0100
parents ddbe8ab9f8a4
children af8fed1a84e7
files src/eval.c src/testdir/test55.ok src/testdir/test_partial.vim src/version.c
diffstat 4 files changed, 57 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -867,7 +867,7 @@ static int valid_varname(char_u *varname
 static int tv_check_lock(int lock, char_u *name, int use_gettext);
 static int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
 static char_u *find_option_end(char_u **arg, int *opt_flags);
-static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd);
+static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd, partial_T **partial);
 static int eval_fname_script(char_u *p);
 static int eval_fname_sid(char_u *p);
 static void list_func_head(ufunc_T *fp, int indent);
@@ -3476,7 +3476,7 @@ ex_call(exarg_T *eap)
 	return;
     }
 
-    tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi);
+    tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial);
     if (fudi.fd_newkey != NULL)
     {
 	/* Still need to give an error message for missing key. */
@@ -3491,9 +3491,18 @@ ex_call(exarg_T *eap)
     if (fudi.fd_dict != NULL)
 	++fudi.fd_dict->dv_refcount;
 
-    /* If it is the name of a variable of type VAR_FUNC use its contents. */
+    /* If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
+     * contents.  For VAR_PARTIAL get its partial, unless we already have one
+     * from trans_function_name(). */
     len = (int)STRLEN(tofree);
-    name = deref_func_name(tofree, &len, &partial, FALSE);
+    name = deref_func_name(tofree, &len,
+				    partial != NULL ? NULL : &partial, FALSE);
+
+    /* When calling fdict.func(), where "func" is a partial, use "fdict"
+     * instead of the dict in the partial, for backwards compatibility.
+     * TODO: Do use the arguments in the partial? */
+    if (fudi.fd_dict != NULL)
+	partial = NULL;
 
     /* Skip white space to allow ":call func ()".  Not good, but required for
      * backward compatibility. */
@@ -8561,15 +8570,17 @@ find_internal_func(
 /*
  * Check if "name" is a variable of type VAR_FUNC.  If so, return the function
  * name it contains, otherwise return "name".
- * If "name" is of type VAR_PARTIAL also return "partial"
+ * If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
+ * "partialp".
  */
     static char_u *
-deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload)
+deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
 {
     dictitem_T	*v;
     int		cc;
 
-    *partial = NULL;
+    if (partialp != NULL)
+	*partialp = NULL;
 
     cc = name[*lenp];
     name[*lenp] = NUL;
@@ -8588,14 +8599,17 @@ deref_func_name(char_u *name, int *lenp,
 
     if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
     {
-	*partial = v->di_tv.vval.v_partial;
-	if (*partial == NULL)
+	partial_T *pt = v->di_tv.vval.v_partial;
+
+	if (pt == NULL)
 	{
 	    *lenp = 0;
 	    return (char_u *)"";	/* just in case */
 	}
-	*lenp = (int)STRLEN((*partial)->pt_name);
-	return (*partial)->pt_name;
+	if (partialp != NULL)
+	    *partialp = pt;
+	*lenp = (int)STRLEN(pt->pt_name);
+	return pt->pt_name;
     }
 
     return name;
@@ -21700,18 +21714,23 @@ handle_subscript(
 
     if (rettv->v_type == VAR_FUNC && selfdict != NULL)
     {
-	partial_T	*pt = (partial_T *)alloc_clear(sizeof(partial_T));
+	ufunc_T	*fp = find_func(rettv->vval.v_string);
 
 	/* Turn "dict.Func" into a partial for "Func" with "dict". */
-	if (pt != NULL)
-	{
-	    pt->pt_refcount = 1;
-	    pt->pt_dict = selfdict;
-	    selfdict = NULL;
-	    pt->pt_name = rettv->vval.v_string;
-	    func_ref(pt->pt_name);
-	    rettv->v_type = VAR_PARTIAL;
-	    rettv->vval.v_partial = pt;
+	if (fp != NULL && (fp->uf_flags & FC_DICT))
+	{
+	    partial_T	*pt = (partial_T *)alloc_clear(sizeof(partial_T));
+
+	    if (pt != NULL)
+	    {
+		pt->pt_refcount = 1;
+		pt->pt_dict = selfdict;
+		selfdict = NULL;
+		pt->pt_name = rettv->vval.v_string;
+		func_ref(pt->pt_name);
+		rettv->v_type = VAR_PARTIAL;
+		rettv->vval.v_partial = pt;
+	    }
 	}
     }
 
@@ -23220,7 +23239,7 @@ ex_function(exarg_T *eap)
      * g:func	    global function name, same as "func"
      */
     p = eap->arg;
-    name = trans_function_name(&p, eap->skip, 0, &fudi);
+    name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
     paren = (vim_strchr(p, '(') != NULL);
     if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
     {
@@ -23533,7 +23552,7 @@ ex_function(exarg_T *eap)
 		if (*p == '!')
 		    p = skipwhite(p + 1);
 		p += eval_fname_script(p);
-		vim_free(trans_function_name(&p, TRUE, 0, NULL));
+		vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL));
 		if (*skipwhite(p) == '(')
 		{
 		    ++nesting;
@@ -23788,7 +23807,8 @@ trans_function_name(
     char_u	**pp,
     int		skip,		/* only find the end, don't evaluate */
     int		flags,
-    funcdict_T	*fdp)		/* return: info about dictionary used */
+    funcdict_T	*fdp,		/* return: info about dictionary used */
+    partial_T	**partial)	/* return: partial of a FuncRef */
 {
     char_u	*name = NULL;
     char_u	*start;
@@ -23797,7 +23817,6 @@ trans_function_name(
     char_u	sid_buf[20];
     int		len;
     lval_T	lv;
-    partial_T	*partial;
 
     if (fdp != NULL)
 	vim_memset(fdp, 0, sizeof(funcdict_T));
@@ -23882,7 +23901,7 @@ trans_function_name(
     if (lv.ll_exp_name != NULL)
     {
 	len = (int)STRLEN(lv.ll_exp_name);
-	name = deref_func_name(lv.ll_exp_name, &len, &partial,
+	name = deref_func_name(lv.ll_exp_name, &len, partial,
 						     flags & TFN_NO_AUTOLOAD);
 	if (name == lv.ll_exp_name)
 	    name = NULL;
@@ -23890,7 +23909,7 @@ trans_function_name(
     else
     {
 	len = (int)(end - *pp);
-	name = deref_func_name(*pp, &len, &partial, flags & TFN_NO_AUTOLOAD);
+	name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
 	if (name == *pp)
 	    name = NULL;
     }
@@ -24115,7 +24134,7 @@ function_exists(char_u *name)
     int	    n = FALSE;
 
     p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD,
-			    NULL);
+			    NULL, NULL);
     nm = skipwhite(nm);
 
     /* Only accept "funcname", "funcname ", "funcname (..." and
@@ -24132,7 +24151,7 @@ get_expanded_name(char_u *name, int chec
     char_u	*nm = name;
     char_u	*p;
 
-    p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL);
+    p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL, NULL);
 
     if (p != NULL && *nm == NUL)
 	if (!check || translated_function_exists(p))
@@ -24488,7 +24507,7 @@ ex_delfunction(exarg_T *eap)
     funcdict_T	fudi;
 
     p = eap->arg;
-    name = trans_function_name(&p, eap->skip, 0, &fudi);
+    name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
     vim_free(fudi.fd_newkey);
     if (name == NULL)
     {
--- a/src/testdir/test55.ok
+++ b/src/testdir/test55.ok
@@ -44,7 +44,7 @@ 33=999
 {'33': 999}
 len: 3
 again: 3
-Vim(call):E725: 
+xxx3
 g:dict.func-4
 a:function('3')
 Vim(let):E698: 
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -16,6 +16,8 @@ endfunc
 
 func Test_partial_args()
   let Cb = function('MyFunc', ["foo", "bar"])
+
+  call Cb("zzz")
   call assert_equal("foo/bar/xxx", Cb("xxx"))
   call assert_equal("foo/bar/yyy", call(Cb, ["yyy"]))
 
@@ -49,6 +51,9 @@ func Test_partial_dict()
   let Cb = function('MyDictFunc', dict)
   call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
   call assert_fails('Cb("fff")', 'E492:')
+
+  let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
+  call assert_equal("Hello", dict.tr())
 endfunc
 
 func Test_partial_implicit()
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1581,
+/**/
     1580,
 /**/
     1579,