Patch 8.2.1105
Problem:    Insufficient test coverage for Lua.
Solution:   Add tests. (Yegappan Lakshmanan, closes #6368)  Fix uncovered
            memory leak.  Avoid unnecessary copy/free.
Files:      src/if_lua.c, src/testdir/test_lua.vim


*** ../vim-8.2.1104/src/if_lua.c        2020-06-28 22:41:23.351349861 +0200
--- src/if_lua.c        2020-07-01 13:49:51.836829466 +0200
***************
*** 936,943 ****
            typval_T v;
            luaV_checktypval(L, 3, &v, "setting list item");
            clear_tv(&li->li_tv);
!           copy_tv(&v, &li->li_tv);
!           clear_tv(&v);
          }
      }
      return 0;
--- 936,942 ----
            typval_T v;
            luaV_checktypval(L, 3, &v, "setting list item");
            clear_tv(&li->li_tv);
!           li->li_tv = v;
          }
      }
      return 0;
***************
*** 1084,1090 ****
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di;
!     typval_T v;
  
      if (d->dv_lock)
        luaL_error(L, "dict is locked");
--- 1083,1089 ----
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
      char_u *key = (char_u *) luaL_checkstring(L, 2);
      dictitem_T *di;
!     typval_T tv;
  
      if (d->dv_lock)
        luaL_error(L, "dict is locked");
***************
*** 1094,1102 ****
        luaL_error(L, "empty key");
      if (!lua_isnil(L, 3)) // read value?
      {
!       luaV_checktypval(L, 3, &v, "setting dict item");
!       if (d->dv_scope == VAR_DEF_SCOPE && v.v_type == VAR_FUNC)
            luaL_error(L, "cannot assign funcref to builtin scope");
      }
      di = dict_find(d, key, -1);
      if (di == NULL) // non-existing key?
--- 1093,1104 ----
        luaL_error(L, "empty key");
      if (!lua_isnil(L, 3)) // read value?
      {
!       luaV_checktypval(L, 3, &tv, "setting dict item");
!       if (d->dv_scope == VAR_DEF_SCOPE && tv.v_type == VAR_FUNC)
!       {
!           clear_tv(&tv);
            luaL_error(L, "cannot assign funcref to builtin scope");
+       }
      }
      di = dict_find(d, key, -1);
      if (di == NULL) // non-existing key?
***************
*** 1105,1114 ****
--- 1107,1120 ----
            return 0;
        di = dictitem_alloc(key);
        if (di == NULL)
+       {
+           clear_tv(&tv);
            return 0;
+       }
        if (dict_add(d, di) == FAIL)
        {
            vim_free(di);
+           clear_tv(&tv);
            return 0;
        }
      }
***************
*** 1121,1130 ****
        dictitem_free(di);
      }
      else
!     {
!       copy_tv(&v, &di->di_tv);
!       clear_tv(&v);
!     }
      return 0;
  }
  
--- 1127,1133 ----
        dictitem_free(di);
      }
      else
!       di->di_tv = tv;
      return 0;
  }
  
***************
*** 1441,1447 ****
                        curwin->w_cursor.lnum -= 1;
                        check_cursor_col();
                    }
!                   else check_cursor();
                    changed_cline_bef_curs();
                }
                invalidate_botline();
--- 1444,1451 ----
                        curwin->w_cursor.lnum -= 1;
                        check_cursor_col();
                    }
!                   else
!                       check_cursor();
                    changed_cline_bef_curs();
                }
                invalidate_botline();
***************
*** 1842,1849 ****
                    lua_pushnil(L);
                    return 1;
                }
!               copy_tv(&v, &di->di_tv);
!               clear_tv(&v);
                lua_pop(L, 2); // key copy and value
            }
        }
--- 1846,1852 ----
                    lua_pushnil(L);
                    return 1;
                }
!               di->di_tv = v;
                lua_pop(L, 2); // key copy and value
            }
        }
***************
*** 2398,2404 ****
      lua_replace(L, -2); // function -> body
      for (l = eap->line1; l <= eap->line2; l++)
      {
!       // Check the line number, the command my have deleted lines.
        if (l > curbuf->b_ml.ml_line_count)
            break;
  
--- 2401,2407 ----
      lua_replace(L, -2); // function -> body
      for (l = eap->line1; l <= eap->line2; l++)
      {
!       // Check the line number, the command may have deleted lines.
        if (l > curbuf->b_ml.ml_line_count)
            break;
  
*** ../vim-8.2.1104/src/testdir/test_lua.vim    2020-06-28 22:41:23.351349861 
+0200
--- src/testdir/test_lua.vim    2020-07-01 13:36:39.216161900 +0200
***************
*** 4,9 ****
--- 4,18 ----
  CheckFeature lua
  CheckFeature float
  
+ let s:luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.')
+ let s:major = str2nr(s:luaver[0])
+ let s:minor = str2nr(s:luaver[1])
+ if s:major < 5 || (s:major == 5 && s:minor < 3)
+   let s:lua_53_or_later = 0
+ else
+   let s:lua_53_or_later = 1
+ endif
+ 
  func TearDown()
    " Run garbage collection after each test to exercise luaV_setref().
    call test_garbagecollect_now()
***************
*** 28,33 ****
--- 37,59 ----
    bwipe!
  endfunc
  
+ func Test_lua_luado()
+   new
+   call setline(1, ['one', 'two'])
+   luado return(linenr)
+   call assert_equal(['1', '2'], getline(1, '$'))
+   close!
+ 
+   " Error cases
+   call assert_fails('luado string.format()',
+         \ "[string \"vim chunk\"]:1: bad argument #1 to 'format' (string 
expected, got no value)")
+   call assert_fails('luado func()',
+         \ s:lua_53_or_later
+         \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (global 
'func')"
+         \ : "[string \"vim chunk\"]:1: attempt to call global 'func' (a nil 
value)")
+   call assert_fails('luado error("failed")', "[string \"vim chunk\"]:1: 
failed")
+ endfunc
+ 
  " Test vim.eval()
  func Test_lua_eval()
    " lua.eval with a number
***************
*** 55,60 ****
--- 81,104 ----
    call assert_equal('blob', luaeval('vim.type(v)'))
    call assert_equal(0z00112233.deadbeef, luaeval('v'))
  
+   " lua.eval with a float
+   lua v = vim.eval('3.14')
+   call assert_equal('number', luaeval('vim.type(v)'))
+   call assert_equal(3.14, luaeval('v'))
+ 
+   " lua.eval with a bool
+   lua v = vim.eval('v:true')
+   call assert_equal('number', luaeval('vim.type(v)'))
+   call assert_equal(1, luaeval('v'))
+   lua v = vim.eval('v:false')
+   call assert_equal('number', luaeval('vim.type(v)'))
+   call assert_equal(0, luaeval('v'))
+ 
+   " lua.eval with a null
+   lua v = vim.eval('v:null')
+   call assert_equal('nil', luaeval('vim.type(v)'))
+   call assert_equal(v:null, luaeval('v'))
+ 
    call assert_fails('lua v = vim.eval(nil)',
          \ "[string \"vim chunk\"]:1: bad argument #1 to 'eval' (string 
expected, got nil)")
    call assert_fails('lua v = vim.eval(true)',
***************
*** 82,87 ****
--- 126,138 ----
    " Window 3 does not exist so vim.window(3) should return nil
    call assert_equal('nil', luaeval('tostring(vim.window(3))'))
  
+   call assert_fails("let n = luaeval('vim.window().xyz()')",
+         \ s:lua_53_or_later
+         \ ? "[string \"luaeval\"]:1: attempt to call a nil value (field 
'xyz')"
+         \ : "[string \"luaeval\"]:1: attempt to call field 'xyz' (a nil 
value)")
+   call assert_fails('lua vim.window().xyz = 1',
+         \ "[string \"vim chunk\"]:1: invalid window property: `xyz'")
+ 
    %bwipe!
  endfunc
  
***************
*** 125,130 ****
--- 176,190 ----
  func Test_lua_call()
    call assert_equal(has('lua'), luaeval('vim.call("has", "lua")'))
    call assert_equal(printf("Hello %s", "vim"), luaeval('vim.call("printf", 
"Hello %s", "vim")'))
+ 
+   " Error cases
+   call assert_fails("call luaeval('vim.call(\"min\", 1, 2, 3, 4, 5, 6, 7, 8, 
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)')",
+         \ '[string "luaeval"]:1: Function called with too many arguments')
+   lua co = coroutine.create(function () print("hi") end)
+   call assert_fails("call luaeval('vim.call(\"type\", co)')",
+         \ '[string "luaeval"]:1: lua: cannot convert value')
+   lua co = nil
+   call assert_fails("call luaeval('vim.call(\"abc\")')", '[string 
"luaeval"]:1: lua: call_vim_function failed')
  endfunc
  
  " Test vim.fn.*
***************
*** 245,250 ****
--- 305,319 ----
    lua vim.buffer():insert('4', 10)
  
    call assert_equal(['1', '2', '3', '4'], getline(1, '$'))
+   call assert_equal('4', luaeval('vim.buffer()[4]'))
+   call assert_equal(v:null, luaeval('vim.buffer()[5]'))
+   call assert_equal(v:null, luaeval('vim.buffer()[{}]'))
+   call assert_fails('lua vim.buffer():xyz()',
+         \ s:lua_53_or_later
+         \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (method 
'xyz')"
+         \ : "[string \"vim chunk\"]:1: attempt to call method 'xyz' (a nil 
value)")
+   call assert_fails('lua vim.buffer()[1] = {}',
+         \ '[string "vim chunk"]:1: wrong argument to change')
    bwipe!
  endfunc
  
***************
*** 252,257 ****
--- 321,327 ----
  func Test_lua_buffer_delete()
    new
    call setline(1, ['1', '2', '3'])
+   call cursor(3, 1)
    lua vim.buffer()[2] = nil
    call assert_equal(['1', '3'], getline(1, '$'))
  
***************
*** 331,340 ****
--- 401,426 ----
    lua l[6] = nil
    lua l:insert('first')
    lua l:insert('xx', 3)
+   call assert_fails('lua l:insert("xx", -20)',
+         \ '[string "vim chunk"]:1: invalid position')
    call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, 
{'a': 1, 'b': 2, 'c': 3}], l)
  
    lockvar 1 l
    call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is 
locked')
+   call assert_fails('lua l:insert(2)', '[string "vim chunk"]:1: list is 
locked')
+   call assert_fails('lua l[9] = 1', '[string "vim chunk"]:1: list is locked')
+ 
+   unlockvar l
+   let l = [1, 2]
+   lua ll = vim.eval('l')
+   let x = luaeval("ll[3]")
+   call assert_equal(v:null, x)
+   call assert_fails('let x = luaeval("ll:xyz(3)")',
+         \ s:lua_53_or_later
+         \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 
'xyz')"
+         \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil 
value)")
+   let y = luaeval("ll[{}]")
+   call assert_equal(v:null, y)
  
    lua l = nil
  endfunc
***************
*** 354,364 ****
  endfunc
  
  func Test_lua_list_table_insert_remove()
!   let luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.')
!   let major = str2nr(luaver[0])
!   let minor = str2nr(luaver[1])
! 
!   if major < 5 || (major == 5 && minor < 3)
      throw 'Skipped: Lua version < 5.3'
    endif
  
--- 440,446 ----
  endfunc
  
  func Test_lua_list_table_insert_remove()
!   if !s:lua_53_or_later
      throw 'Skipped: Lua version < 5.3'
    endif
  
***************
*** 429,434 ****
--- 511,517 ----
    call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)'))
  
    call assert_equal('abc', luaeval('d[1]'))
+   call assert_equal(v:null, luaeval('d[22]'))
  
    lua d[0] = 124
    lua d[4] = nil
***************
*** 436,441 ****
--- 519,541 ----
  
    lockvar 1 d
    call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked')
+   unlockvar d
+ 
+   " Error case
+   lua d = {}
+   lua d[''] = 10
+   call assert_fails("let t = luaeval('vim.dict(d)')",
+         \ '[string "luaeval"]:1: table has empty key')
+   let d = {}
+   lua x = vim.eval('d')
+   call assert_fails("lua x[''] = 10", '[string "vim chunk"]:1: empty key')
+   lua x['a'] = nil
+   call assert_equal({}, d)
+ 
+   " cannot assign funcrefs in the global scope
+   lua x = vim.eval('g:')
+   call assert_fails("lua x['min'] = vim.funcref('max')",
+         \ '[string "vim chunk"]:1: cannot assign funcref to builtin scope')
  
    lua d = nil
  endfunc
***************
*** 493,498 ****
--- 593,620 ----
    call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string 
expected, got boolean')
    call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string 
expected, got table')
    lua b = nil
+ 
+   let b = 0z0102
+   lua lb = vim.eval('b')
+   let n = luaeval('lb[1]')
+   call assert_equal(2, n)
+   let n = luaeval('lb[6]')
+   call assert_equal(v:null, n)
+   call assert_fails('let x = luaeval("lb:xyz(3)")',
+         \ s:lua_53_or_later
+         \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 
'xyz')"
+         \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil 
value)")
+   let y = luaeval("lb[{}]")
+   call assert_equal(v:null, y)
+ 
+   lockvar b
+   call assert_fails('lua lb[1] = 2', '[string "vim chunk"]:1: blob is locked')
+   call assert_fails('lua lb:add("12")', '[string "vim chunk"]:1: blob is 
locked')
+ 
+   " Error cases
+   lua t = {}
+   call assert_fails('lua b = vim.blob(t)',
+         \ '[string "vim chunk"]:1: string expected, got table')
  endfunc
  
  func Test_lua_funcref()
***************
*** 506,511 ****
--- 628,644 ----
    lua msg = vim.funcref"tr"(msg, "|", " ")
    call assert_equal("funcref test OK", luaeval('msg'))
  
+   " Error cases
+   call assert_fails('lua f1 = vim.funcref("")',
+         \ '[string "vim chunk"]:1: invalid function name: ')
+   call assert_fails('lua f1 = vim.funcref("10")',
+         \ '[string "vim chunk"]:1: invalid function name: 10')
+   let fname = test_null_string()
+   call assert_fails('lua f1 = vim.funcref(fname)',
+         \ "[string \"vim chunk\"]:1: bad argument #1 to 'funcref' (string 
expected, got nil)")
+   call assert_fails('lua vim.funcref("abc")()',
+         \ '[string "vim chunk"]:1: cannot call funcref')
+ 
    " dict funcref
    function Mylen() dict
      return len(self.data)
***************
*** 619,624 ****
--- 752,761 ----
    \ '[string "luaeval"]:1: attempt to perform arithmetic on a nil value')
    call assert_fails("call luaeval(']')",
    \ "[string \"luaeval\"]:1: unexpected symbol near ']'")
+   lua co = coroutine.create(function () print("hi") end)
+   call assert_fails('let i = luaeval("co")', 'luaeval: cannot convert value')
+   lua co = nil
+   call assert_fails('let m = luaeval("{}")', 'luaeval: cannot convert value')
  endfunc
  
  " Test :luafile foo.lua
***************
*** 664,669 ****
--- 801,817 ----
    bwipe!
  endfunc
  
+ " Test for dealing with strings containing newlines and null character
+ func Test_lua_string_with_newline()
+   let x = execute('lua print("Hello\nWorld")')
+   call assert_equal("\nHello\nWorld", x)
+   new
+   lua k = vim.buffer(vim.eval('bufnr()'))
+   lua k:insert("Hello\0World", 0)
+   call assert_equal(["Hello\nWorld", ''], getline(1, '$'))
+   close!
+ endfunc
+ 
  func Test_lua_set_cursor()
    " Check that setting the cursor position works.
    new
*** ../vim-8.2.1104/src/version.c       2020-07-01 13:15:21.414343245 +0200
--- src/version.c       2020-07-01 13:22:51.899924673 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1105,
  /**/

-- 
"I've been teaching myself to play the piano for about 5 years and now write
most of my songs on it, mainly because I can never find any paper."
                Jeff Lynne, ELO's greatest hits

 /// 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/202007011153.061BrJVU1139492%40masaka.moolenaar.net.

Raspunde prin e-mail lui