changeset 9183:988c8ab557bf v7.4.1875

commit https://github.com/vim/vim/commit/8e759ba8651428995b338b66c615367259f79766 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jun 2 17:46:20 2016 +0200 patch 7.4.1875 Problem: Comparing functions and partials doesn't work well. Solution: Add tests. (Nikolai Pavlov) Compare the dict and arguments in the partial. (closes https://github.com/vim/vim/issues/813)
author Christian Brabandt <cb@256bit.org>
date Thu, 02 Jun 2016 18:00:07 +0200
parents d0a54c4069ab
children fc6c7e94514b
files src/eval.c src/testdir/test_partial.vim src/version.c
diffstat 3 files changed, 152 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -4627,7 +4627,26 @@ eval4(char_u **arg, typval_T *rettv, int
 		    clear_tv(&var2);
 		    return FAIL;
 		}
-		n1 = tv_equal(rettv, &var2, FALSE, FALSE);
+		if ((rettv->v_type == VAR_PARTIAL
+					     && rettv->vval.v_partial == NULL)
+			|| (var2.v_type == VAR_PARTIAL
+					      && var2.vval.v_partial == NULL))
+		    /* when a partial is NULL assume not equal */
+		    n1 = FALSE;
+		else if (type_is)
+		{
+		    if (rettv->v_type == VAR_FUNC && var2.v_type == VAR_FUNC)
+			/* strings are considered the same if their value is
+			 * the same */
+			n1 = tv_equal(rettv, &var2, ic, FALSE);
+		    else if (rettv->v_type == VAR_PARTIAL
+						&& var2.v_type == VAR_PARTIAL)
+			n1 = (rettv->vval.v_partial == var2.vval.v_partial);
+		    else
+			n1 = FALSE;
+		}
+		else
+		    n1 = tv_equal(rettv, &var2, ic, FALSE);
 		if (type == TYPE_NEQUAL)
 		    n1 = !n1;
 	    }
@@ -6258,6 +6277,58 @@ dict_equal(
 
 static int tv_equal_recurse_limit;
 
+    static int
+func_equal(
+    typval_T *tv1,
+    typval_T *tv2,
+    int	     ic)	    /* ignore case */
+{
+    char_u	*s1, *s2;
+    dict_T	*d1, *d2;
+    int		a1, a2;
+    int		i;
+
+    /* empty and NULL function name considered the same */
+    s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
+					   : tv1->vval.v_partial->pt_name;
+    if (s1 != NULL && *s1 == NUL)
+	s1 = NULL;
+    s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
+					   : tv2->vval.v_partial->pt_name;
+    if (s2 != NULL && *s2 == NUL)
+	s2 = NULL;
+    if (s1 == NULL || s2 == NULL)
+    {
+	if (s1 != s2)
+	    return FALSE;
+    }
+    else if (STRCMP(s1, s2) != 0)
+	return FALSE;
+
+    /* empty dict and NULL dict is different */
+    d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
+    d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
+    if (d1 == NULL || d2 == NULL)
+    {
+	if (d1 != d2)
+	    return FALSE;
+    }
+    else if (!dict_equal(d1, d2, ic, TRUE))
+	return FALSE;
+
+    /* empty list and no list considered the same */
+    a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
+    a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
+    if (a1 != a2)
+	return FALSE;
+    for (i = 0; i < a1; ++i)
+	if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
+		      tv2->vval.v_partial->pt_argv + i, ic, TRUE))
+	    return FALSE;
+
+    return TRUE;
+}
+
 /*
  * Return TRUE if "tv1" and "tv2" have the same value.
  * Compares the items just like "==" would compare them, but strings and
@@ -6275,22 +6346,6 @@ tv_equal(
     static int  recursive_cnt = 0;	    /* catch recursive loops */
     int		r;
 
-    /* For VAR_FUNC and VAR_PARTIAL only compare the function name. */
-    if ((tv1->v_type == VAR_FUNC
-		|| (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
-	    && (tv2->v_type == VAR_FUNC
-		|| (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL)))
-    {
-	s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
-					       : tv1->vval.v_partial->pt_name;
-	s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
-					       : tv2->vval.v_partial->pt_name;
-	return (s1 != NULL && s2 != NULL && STRCMP(s1, s2) == 0);
-    }
-
-    if (tv1->v_type != tv2->v_type)
-	return FALSE;
-
     /* Catch lists and dicts that have an endless loop by limiting
      * recursiveness to a limit.  We guess they are equal then.
      * A fixed limit has the problem of still taking an awful long time.
@@ -6305,6 +6360,21 @@ tv_equal(
 	return TRUE;
     }
 
+    /* For VAR_FUNC and VAR_PARTIAL only compare the function name. */
+    if ((tv1->v_type == VAR_FUNC
+		|| (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
+	    && (tv2->v_type == VAR_FUNC
+		|| (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL)))
+    {
+	++recursive_cnt;
+	r = func_equal(tv1, tv2, ic);
+	--recursive_cnt;
+	return r;
+    }
+
+    if (tv1->v_type != tv2->v_type)
+	return FALSE;
+
     switch (tv1->v_type)
     {
 	case VAR_LIST:
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -316,3 +316,66 @@ func Test_get_partial_items()
   call assert_equal([], get(Func, 'args'))
   call assert_true(empty( get(Func, 'dict')))
 endfunc
+
+func Test_compare_partials()
+  let d1 = {}
+  let d2 = {}
+
+  function d1.f1() dict
+  endfunction
+
+  function d1.f2() dict
+  endfunction
+
+  let F1 = get(d1, 'f1')
+  let F2 = get(d1, 'f2')
+
+  let F1d1 = function(F1, d1)
+  let F2d1 = function(F2, d2)
+  let F1d1a1 = function(F1d1, [1])
+  let F1d1a12 = function(F1d1, [1, 2])
+  let F1a1 = function(F1, [1])
+  let F1a2 = function(F1, [2])
+  let F1d2 = function(F1, d2)
+  let d3 = {'f1': F1, 'f2': F2}
+  let F1d3 = function(F1, d3)
+  let F1ad1 = function(F1, [d1])
+  let F1ad3 = function(F1, [d3])
+
+  call assert_match('^function(''\d\+'')$', string(F1))  " Not a partial
+  call assert_match('^function(''\d\+'')$', string(F2))  " Not a partial
+  call assert_match('^function(''\d\+'', {.*})$', string(F1d1))  " A partial
+  call assert_match('^function(''\d\+'', {.*})$', string(F2d1))  " A partial
+  call assert_match('^function(''\d\+'', \[.*\])$', string(F1a1))  " No dict
+
+  " !=
+  let X = F1
+  call assert_false(F1 != X)  " same function
+  let X = F1d1
+  call assert_false(F1d1 != X)  " same partial
+  let X = F1d1a1
+  call assert_false(F1d1a1 != X)  " same partial
+  let X = F1a1
+  call assert_false(F1a1 != X)  " same partial
+
+  call assert_true(F1 != F2)  " Different functions
+  call assert_true(F1 != F1d1)  " Partial /= non-partial
+  call assert_true(F1d1a1 != F1d1a12)  " Different number of arguments
+  call assert_true(F1a1 != F1d1a12)  " One has no dict
+  call assert_true(F1a1 != F1a2)  " Different arguments
+  call assert_true(F1d2 != F1d1)  " Different dictionaries
+  call assert_false(F1d1 != F1d3)  " Equal dictionaries, even though d1 isnot d3
+
+  " isnot, option 1
+  call assert_true(F1 isnot# F2)  " Different functions
+  call assert_true(F1 isnot# F1d1)  " Partial /= non-partial
+  call assert_true(F1d1 isnot# F1d3)  " d1 isnot d3, even though d1 == d3
+  call assert_true(F1a1 isnot# F1d1a12)  " One has no dict
+  call assert_true(F1a1 isnot# F1a2)  " Different number of arguments
+  call assert_true(F1ad1 isnot# F1ad3)  " In arguments d1 isnot d3
+
+  " isnot, option 2
+  call assert_true(F1 isnot# F2)  " Different functions
+  call assert_true(F1 isnot# F1d1)  " Partial /= non-partial
+  call assert_true(d1.f1 isnot# d1.f1)  " handle_subscript creates new partial each time
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1875,
+/**/
     1874,
 /**/
     1873,