Patch 8.2.0619
Problem:    Null dict is not handled like an empty dict.
Solution:   Fix the code and add tests. (Yegappan Lakshmanan, closes #5968)
Files:      src/dict.c, src/eval.c, src/testdir/test_blob.vim,
            src/testdir/test_expr.vim, src/testdir/test_filter_map.vim,
            src/testdir/test_let.vim, src/testdir/test_listdict.vim,
            src/testdir/test_search.vim, src/testdir/test_unlet.vim,
            src/testdir/test_usercommands.vim, src/testdir/test_vimscript.vim


*** ../vim-8.2.0618/src/dict.c  2020-03-28 19:41:29.595765241 +0100
--- src/dict.c  2020-04-23 13:01:08.432900291 +0200
***************
*** 977,990 ****
      dictitem_T        *item2;
      int               todo;
  
-     if (d1 == NULL && d2 == NULL)
-       return TRUE;
-     if (d1 == NULL || d2 == NULL)
-       return FALSE;
      if (d1 == d2)
        return TRUE;
      if (dict_len(d1) != dict_len(d2))
        return FALSE;
  
      todo = (int)d1->dv_hashtab.ht_used;
      for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
--- 977,991 ----
      dictitem_T        *item2;
      int               todo;
  
      if (d1 == d2)
        return TRUE;
      if (dict_len(d1) != dict_len(d2))
        return FALSE;
+     if (dict_len(d1) == 0)
+       // empty and NULL dicts are considered equal
+       return TRUE;
+     if (d1 == NULL || d2 == NULL)
+       return FALSE;
  
      todo = (int)d1->dv_hashtab.ht_used;
      for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
*** ../vim-8.2.0618/src/eval.c  2020-04-22 19:13:16.043410511 +0200
--- src/eval.c  2020-04-23 13:15:55.054069438 +0200
***************
*** 871,877 ****
--- 871,880 ----
                if (len != -1)
                    key[len] = prevval;
                if (wrong)
+               {
+                   clear_tv(&var1);
                    return NULL;
+               }
            }
  
            if (lp->ll_di == NULL)
***************
*** 4553,4560 ****
        case VAR_DICT:
            if (tv->vval.v_dict == NULL)
            {
                *tofree = NULL;
!               r = NULL;
            }
            else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID
                    && tv->vval.v_dict->dv_hashtab.ht_used != 0)
--- 4556,4564 ----
        case VAR_DICT:
            if (tv->vval.v_dict == NULL)
            {
+               // NULL dict is equivalent to empty dict.
                *tofree = NULL;
!               r = (char_u *)"{}";
            }
            else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID
                    && tv->vval.v_dict->dv_hashtab.ht_used != 0)
***************
*** 4565,4570 ****
--- 4569,4575 ----
            else
            {
                int old_copyID = tv->vval.v_dict->dv_copyID;
+ 
                tv->vval.v_dict->dv_copyID = copyID;
                *tofree = dict2string(tv, copyID, restore_copyID);
                if (restore_copyID)
*** ../vim-8.2.0618/src/testdir/test_blob.vim   2020-04-21 22:19:26.055486850 
+0200
--- src/testdir/test_blob.vim   2020-04-23 13:01:08.432900291 +0200
***************
*** 207,212 ****
--- 207,213 ----
    call assert_equal(0z001122, b)
    call add(b, '51')
    call assert_equal(0z00112233, b)
+   call assert_equal(1, add(test_null_blob(), 0x22))
  
    call assert_fails('call add(b, [9])', 'E745:')
    call assert_fails('call add("", 0x01)', 'E897:')
*** ../vim-8.2.0618/src/testdir/test_expr.vim   2020-04-20 16:49:56.705830066 
+0200
--- src/testdir/test_expr.vim   2020-04-23 13:01:08.432900291 +0200
***************
*** 96,108 ****
    endfor
  endfunc
  
- func Test_compare_null_dict()
-   call assert_fails('let x = test_null_dict()[10]')
-   call assert_equal({}, {})
-   call assert_equal(test_null_dict(), test_null_dict())
-   call assert_notequal({}, test_null_dict())
- endfunc
- 
  func Test_set_reg_null_list()
    call setreg('x', test_null_list())
  endfunc
--- 96,101 ----
*** ../vim-8.2.0618/src/testdir/test_filter_map.vim     2020-04-20 
16:49:56.705830066 +0200
--- src/testdir/test_filter_map.vim     2020-04-23 13:01:08.432900291 +0200
***************
*** 96,101 ****
--- 96,102 ----
    call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E712:')
    call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:')
    call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:')
+   call assert_fails("let l = filter([1, 2], {})", 'E731:')
    call assert_equal(0, map(test_null_list(), '"> " .. v:val'))
    call assert_equal(0, map(test_null_dict(), '"> " .. v:val'))
  endfunc
*** ../vim-8.2.0618/src/testdir/test_let.vim    2020-03-20 18:20:47.080975621 
+0100
--- src/testdir/test_let.vim    2020-04-23 13:01:08.432900291 +0200
***************
*** 274,279 ****
--- 274,280 ----
    call assert_fails('let &buftype[1] = "nofile"', 'E18:')
    let s = "var"
    let var = 1
+   call assert_fails('let var += [1,2]', 'E734:')
    call assert_fails('let {s}.1 = 2', 'E18:')
    call assert_fails('let a[1] = 5', 'E121:')
    let l = [[1,2]]
***************
*** 286,291 ****
--- 287,294 ----
    call assert_fails('let l[0:1] = [1, 2, 3]', 'E710:')
    call assert_fails('let l[-2:-3] = [3, 4]', 'E684:')
    call assert_fails('let l[0:4] = [5, 6]', 'E711:')
+   call assert_fails('let g:["a;b"] = 10', 'E461:')
+   call assert_fails('let g:.min = function("max")', 'E704:')
  
    " This test works only when the language is English
    if v:lang == "C" || v:lang =~ '^[Ee]n'
*** ../vim-8.2.0618/src/testdir/test_listdict.vim       2020-04-20 
16:49:56.705830066 +0200
--- src/testdir/test_listdict.vim       2020-04-23 13:01:08.432900291 +0200
***************
*** 32,38 ****
    call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], 
l[0:8])
    call assert_equal([], l[8:-1])
    call assert_equal([], l[0:-10])
!   call assert_equal([], test_null_list()[:2])
  endfunc
  
  " List identity
--- 32,42 ----
    call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], 
l[0:8])
    call assert_equal([], l[8:-1])
    call assert_equal([], l[0:-10])
!   " perform an operation on a list slice
!   let l = [1, 2, 3]
!   let l[:1] += [1, 2]
!   let l[2:] -= [1]
!   call assert_equal([2, 4, 2], l)
  endfunc
  
  " List identity
***************
*** 147,152 ****
--- 151,170 ----
    call assert_fails("call remove(l, l)", 'E745:')
  endfunc
  
+ " List add() function
+ func Test_list_add()
+   let l = []
+   call add(l, 1)
+   call add(l, [2, 3])
+   call add(l, [])
+   call add(l, test_null_list())
+   call add(l, {'k' : 3})
+   call add(l, {})
+   call add(l, test_null_dict())
+   call assert_equal([1, [2, 3], [], [], {'k' : 3}, {}, {}], l)
+   call assert_equal(1, add(test_null_list(), 4))
+ endfunc
+ 
  " Tests for Dictionary type
  
  func Test_dict()
***************
*** 529,534 ****
--- 547,561 ----
    call assert_equal({'a': 99, 'b': 100}, d)
  endfunc
  
+ " Cannot use += with a locked dick
+ func Test_dict_lock_operator()
+   unlet! d
+   let d = {}
+   lockvar d
+   call assert_fails("let d += {'k' : 10}", 'E741:')
+   unlockvar d
+ endfunc
+ 
  " No remove() of write-protected scope-level variable
  func Tfunc1(this_is_a_long_parameter_name)
    call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 
'E742')
***************
*** 651,658 ****
    call assert_fails("call sort([1, 2], function('min'), 1)", "E715:")
    call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:")
    call assert_fails("call sort([1, 2], function('min'))", "E702:")
-   call assert_equal(0, sort(test_null_list()))
-   call assert_equal(0, uniq(test_null_list()))
  endfunc
  
  " splitting a string to a List using split()
--- 678,683 ----
***************
*** 896,918 ****
    call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:')
    call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:')
    call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:')
  endfunc
  
  " Test for a null list
  func Test_null_list()
!   call assert_equal(0, join(test_null_list()))
    call assert_fails('let s = join([1, 2], [])', 'E730:')
    call assert_equal([], split(test_null_string()))
  endfunc
  
  " Test for a null dict
  func Test_null_dict()
!   call assert_equal(0, items(test_null_dict()))
!   call assert_equal(0, keys(test_null_dict()))
!   call assert_equal(0, values(test_null_dict()))
!   call assert_false(has_key(test_null_dict(), 'k'))
!   call assert_fails("let l = [] + test_null_list()", 'E15:')
!   call assert_fails("let l = test_null_list() + []", 'E15:')
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
--- 921,964 ----
    call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:')
    call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:')
    call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:')
+   let l = [1, 2, 3]
+   call assert_fails("let l[i] = 3", 'E121:')
+   call assert_fails("let l[1.1] = 4", 'E806:')
+   call assert_fails("let l[:i] = [4, 5]", 'E121:')
+   call assert_fails("let l[:3.2] = [4, 5]", 'E806:')
  endfunc
  
  " Test for a null list
  func Test_null_list()
!   let l = test_null_list()
!   call assert_equal(0, join(l))
!   call assert_equal(0, len(l))
!   call assert_equal(1, empty(l))
    call assert_fails('let s = join([1, 2], [])', 'E730:')
    call assert_equal([], split(test_null_string()))
+   call assert_equal([], l[:2])
+   call assert_true([] == l)
+   call assert_equal('[]', string(l))
+   call assert_equal(0, sort(l))
+   call assert_equal(0, uniq(l))
+   call assert_fails("let k = [] + l", 'E15:')
+   call assert_fails("let k = l + []", 'E15:')
  endfunc
  
  " Test for a null dict
  func Test_null_dict()
!   call assert_equal(test_null_dict(), test_null_dict())
!   let d = test_null_dict()
!   call assert_equal({}, d)
!   call assert_equal(0, len(d))
!   call assert_equal(1, empty(d))
!   call assert_equal(0, items(d))
!   call assert_equal(0, keys(d))
!   call assert_equal(0, values(d))
!   call assert_false(has_key(d, 'k'))
!   call assert_equal('{}', string(d))
!   call assert_fails('let x = test_null_dict()[10]')
!   call assert_equal({}, {})
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.0618/src/testdir/test_search.vim 2020-04-11 18:36:35.047150850 
+0200
--- src/testdir/test_search.vim 2020-04-23 13:01:08.432900291 +0200
***************
*** 1617,1620 ****
--- 1617,1651 ----
    close!
  endfunc
  
+ " Test for searching with 'smartcase' and 'ignorecase'
+ func Test_search_smartcase()
+   new
+   call setline(1, ['', 'Hello'])
+   set noignorecase nosmartcase
+   call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+ 
+   set ignorecase nosmartcase
+   exe "normal /\\a\\_.\\(.*\\)O\<CR>"
+   call assert_equal([2, 1], [line('.'), col('.')])
+ 
+   call cursor(1, 1)
+   set ignorecase smartcase
+   call assert_fails('exe "normal /\\a\\_.\\(.*\\)O\<CR>"', 'E486:')
+ 
+   exe "normal /\\a\\_.\\(.*\\)o\<CR>"
+   call assert_equal([2, 1], [line('.'), col('.')])
+ 
+   set ignorecase& smartcase&
+   close!
+ endfunc
+ 
+ " Test searching past the end of a file
+ func Test_search_past_eof()
+   new
+   call setline(1, ['Line'])
+   exe "normal /\\n\\zs\<CR>"
+   call assert_equal([1, 4], [line('.'), col('.')])
+   close!
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.0618/src/testdir/test_unlet.vim  2020-03-20 18:20:47.080975621 
+0100
--- src/testdir/test_unlet.vim  2020-04-23 13:01:08.432900291 +0200
***************
*** 27,32 ****
--- 27,33 ----
    call assert_fails("unlet l['k'", 'E111:')
    let d = {'k' : 1}
    call assert_fails("unlet d.k2", 'E716:')
+   call assert_fails("unlet {a};", 'E488:')
  endfunc
  
  func Test_unlet_env()
*** ../vim-8.2.0618/src/testdir/test_usercommands.vim   2020-04-13 
21:16:18.039292270 +0200
--- src/testdir/test_usercommands.vim   2020-04-23 13:01:08.432900291 +0200
***************
*** 587,603 ****
      return "a\nb\n"
    endfunc
    command -nargs=* -complete=customlist,T1 TCmd1
!   call feedkeys(":T1 \<C-A>\<C-B>\"\<CR>", 'xt')
!   call assert_equal('"T1 ', @:)
    delcommand TCmd1
    delfunc T1
  
    func T2(a, c, p)
!     return ['a', 'b', 'c']
    endfunc
    command -nargs=* -complete=customlist,T2 TCmd2
!   call feedkeys(":T2 \<C-A>\<C-B>\"\<CR>", 'xt')
!   call assert_equal('"T2 ', @:)
    delcommand TCmd2
    delfunc T2
  endfunc
--- 587,603 ----
      return "a\nb\n"
    endfunc
    command -nargs=* -complete=customlist,T1 TCmd1
!   call feedkeys(":TCmd1 \<C-A>\<C-B>\"\<CR>", 'xt')
!   call assert_equal('"TCmd1 ', @:)
    delcommand TCmd1
    delfunc T1
  
    func T2(a, c, p)
!     return {}
    endfunc
    command -nargs=* -complete=customlist,T2 TCmd2
!   call feedkeys(":TCmd2 \<C-A>\<C-B>\"\<CR>", 'xt')
!   call assert_equal('"TCmd2 ', @:)
    delcommand TCmd2
    delfunc T2
  endfunc
*** ../vim-8.2.0618/src/testdir/test_vimscript.vim      2020-04-20 
16:49:56.705830066 +0200
--- src/testdir/test_vimscript.vim      2020-04-23 13:01:08.432900291 +0200
***************
*** 1671,1676 ****
--- 1671,1690 ----
        call assert_fails('let x .= "f"', 'E734')
        let x = !3.14
        call assert_equal(0.0, x)
+ 
+       " integer and float operations
+       let x = 1
+       let x *= 2.1
+       call assert_equal(2.1, x)
+       let x = 1
+       let x /= 0.25
+       call assert_equal(4.0, x)
+       let x = 1
+       call assert_fails('let x %= 0.25', 'E734:')
+       let x = 1
+       call assert_fails('let x .= 0.25', 'E734:')
+       let x = 1.0
+       call assert_fails('let x += [1.1]', 'E734:')
      endif
  
      " Test for environment variable
***************
*** 1871,1876 ****
--- 1885,1893 ----
  
    " Missing 'in' in a :for statement
    call assert_fails('for i range(1) | endfor', 'E690:')
+ 
+   " Incorrect number of variables in for
+   call assert_fails('for [i,] in range(3) | endfor', 'E475:')
  endfunc
  
  " Test for deep nesting of if/for/while/try statements              {{{1
*** ../vim-8.2.0618/src/version.c       2020-04-22 19:13:16.047410502 +0200
--- src/version.c       2020-04-23 13:17:07.645870595 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     619,
  /**/

-- 
Contrary to popular belief, it's often your clothing that gets promoted, not
you.
                                (Scott Adams - The Dilbert principle)

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202004231138.03NBcfa0002075%40masaka.moolenaar.net.

Raspunde prin e-mail lui