diff options
-rw-r--r-- | CONTRIBUTING.md | 17 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | runtime/lua/nvim/health.lua | 2 | ||||
-rw-r--r-- | src/nvim/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/nvim/api/private/converter.c | 3 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 6 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 6 | ||||
-rw-r--r-- | src/nvim/lua/converter.c | 52 | ||||
-rw-r--r-- | src/nvim/lua/converter.h | 6 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 12 | ||||
-rw-r--r-- | src/nvim/lua/stdlib.c | 2 | ||||
-rw-r--r-- | src/nvim/match.c | 4 | ||||
-rw-r--r-- | test/functional/api/autocmd_spec.lua | 38 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 20 | ||||
-rw-r--r-- | test/old/testdir/test_listdict.vim | 6 | ||||
-rw-r--r-- | test/old/testdir/test_match.vim | 5 |
16 files changed, 127 insertions, 63 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b10289a694..88d2a99029 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ If you want to help but don't know where to start, here are some low-risk/isolated tasks: - Try a [complexity:low] issue. -- Fix bugs found by [Clang](#clang-scan-build) or [Coverity](#coverity). +- Fix bugs found by [Coverity](#coverity). - [Merge a Vim patch] (requires strong familiarity with Vim) - NOTE: read the above link before sending improvements to "runtime files" (anything in `runtime/`). - Vimscript and documentation files are (mostly) maintained by [Vim](https://github.com/vim/vim), not Nvim. @@ -132,21 +132,6 @@ Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Acti - To see CI results faster in your PR, you can temporarily set `TEST_FILE` in [test.yml](https://github.com/neovim/neovim/blob/e35b9020b16985eee26e942f9a3f6b045bc3809b/.github/workflows/test.yml#L29). -### Clang scan-build - -View the [Clang report] to see potential bugs found by the Clang -[scan-build](https://clang-analyzer.llvm.org/scan-build.html) analyzer. - -- Search the Neovim commit history to find examples: - ```bash - git log --oneline --no-merges --grep clang - ``` -- To verify a fix locally, run `scan-build` like this: - ```bash - rm -rf build/ - scan-build --use-analyzer=/usr/bin/clang make - ``` - ### Coverity Coverity runs against the master build. To view the defects you must @@ -6,7 +6,6 @@ </h1> [](https://scan.coverity.com/projects/2227) -[](https://neovim.io/doc/reports/clang) [](https://repology.org/metapackage/neovim) [](https://buildd.debian.org/neovim) [](https://github.com/neovim/neovim/releases/) diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/nvim/health.lua index d4670eab6c..5bc03199ee 100644 --- a/runtime/lua/nvim/health.lua +++ b/runtime/lua/nvim/health.lua @@ -50,7 +50,7 @@ local function check_config() local init_lua = vim.fn.stdpath('config') .. '/init.lua' local init_vim = vim.fn.stdpath('config') .. '/init.vim' - local vimrc = vim.env.MYVIMRC or init_lua + local vimrc = vim.env.MYVIMRC and vim.fn.expand(vim.env.MYVIMRC) or init_lua if vim.fn.filereadable(vimrc) == 0 and vim.fn.filereadable(init_vim) == 0 then ok = false diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 45ad738255..64bd40f5f0 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -833,9 +833,12 @@ add_glob_target( FLAGS --quiet EXCLUDE ${EXCLUDE_CLANG_TIDY}) -# These are the same warnings as https://neovim.io/doc/reports/clang/. The -# checks we ignore are meant to be removed eventually, but we can only do so -# after we properly fix the problems without breaking CI. +# The checks we ignore are meant to be removed eventually, but we can only +# enable each warning after we fix all instances of that specific warning as to +# not break CI. +if(APPLE) + string(APPEND CLANG_ANALYZER_IGNORE "-clang-analyzer-core.NonNullParamChecker,") +endif() add_glob_target( TARGET clang-analyzer COMMAND ${CLANG_TIDY_PRG} @@ -849,6 +852,7 @@ add_glob_target( -clang-analyzer-core.uninitialized.Assign, -clang-analyzer-optin.performance.Padding, -clang-analyzer-security.insecureAPI.strcpy, + ${CLANG_ANALYZER_IGNORE} ' EXCLUDE ${EXCLUDE_CLANG_TIDY}) diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index a70ef1e50b..a78d78c057 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -76,8 +76,7 @@ static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t le do { \ ufunc_T *fp = find_func(fun); \ if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { \ - LuaRef ref = api_new_luaref(fp->uf_luaref); \ - kvi_push(edata->stack, LUAREF_OBJ(ref)); \ + kvi_push(edata->stack, LUAREF_OBJ(api_new_luaref(fp->uf_luaref))); \ } else { \ TYPVAL_ENCODE_CONV_NIL(tv); \ } \ diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d3fe2c7e33..8e62e3b96f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2019,6 +2019,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, action = tv_get_string_chk(&argvars[2]); if (action == NULL) { + if (is_new) { + tv_dict_unref(d1); + } return; // Type error; error message already given. } size_t i; @@ -2028,6 +2031,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, } } if (i == 3) { + if (is_new) { + tv_dict_unref(d1); + } semsg(_(e_invarg2), action); return; } diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index e9bc5e5fe3..b0f169ea6f 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -913,7 +913,7 @@ exit_0: write_shifted_output(string.format( [[ if (lua_gettop(lstate) == 0) { - nlua_push_%s(lstate, %sret, true); + nlua_push_%s(lstate, %sret, kNluaPushSpecial | kNluaPushFreeRefs); } ]], return_type, @@ -927,10 +927,10 @@ exit_0: else local special = (fn.since ~= nil and fn.since < 11) write_shifted_output( - ' nlua_push_%s(lstate, %sret, %s);\n', + ' nlua_push_%s(lstate, %sret, %s | kNluaPushFreeRefs);\n', return_type, ret_mode, - tostring(special) + special and 'kNluaPushSpecial' or '0' ) end diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index bba771f8a5..38ccb03cfc 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -597,9 +597,9 @@ static bool typval_conv_special = false; /// @param[in] tv typval_T to convert. /// /// @return true in case of success, false otherwise. -bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) +bool nlua_push_typval(lua_State *lstate, typval_T *const tv, int flags) { - typval_conv_special = special; + typval_conv_special = (flags & kNluaPushSpecial); const int initial_size = lua_gettop(lstate); if (!lua_checkstack(lstate, initial_size + 2)) { @@ -662,7 +662,7 @@ static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, /// Convert given String to lua string /// /// Leaves converted string on top of the stack. -void nlua_push_String(lua_State *lstate, const String s, bool special) +void nlua_push_String(lua_State *lstate, const String s, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushlstring(lstate, s.data, s.size); @@ -671,7 +671,7 @@ void nlua_push_String(lua_State *lstate, const String s, bool special) /// Convert given Integer to lua number /// /// Leaves converted number on top of the stack. -void nlua_push_Integer(lua_State *lstate, const Integer n, bool special) +void nlua_push_Integer(lua_State *lstate, const Integer n, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushnumber(lstate, (lua_Number)n); @@ -680,10 +680,10 @@ void nlua_push_Integer(lua_State *lstate, const Integer n, bool special) /// Convert given Float to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Float(lua_State *lstate, const Float f, bool special) +void nlua_push_Float(lua_State *lstate, const Float f, int flags) FUNC_ATTR_NONNULL_ALL { - if (special) { + if (flags & kNluaPushSpecial) { nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); nlua_push_val_idx(lstate); lua_pushnumber(lstate, (lua_Number)f); @@ -696,7 +696,7 @@ void nlua_push_Float(lua_State *lstate, const Float f, bool special) /// Convert given Float to lua boolean /// /// Leaves converted value on top of the stack. -void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) +void nlua_push_Boolean(lua_State *lstate, const Boolean b, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushboolean(lstate, b); @@ -705,21 +705,21 @@ void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) /// Convert given Dictionary to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special) +void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, int flags) FUNC_ATTR_NONNULL_ALL { - if (dict.size == 0 && special) { + if (dict.size == 0 && (flags & kNluaPushSpecial)) { nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); } else { lua_createtable(lstate, 0, (int)dict.size); - if (dict.size == 0 && !special) { + if (dict.size == 0 && !(flags & kNluaPushSpecial)) { nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); lua_setmetatable(lstate, -2); } } for (size_t i = 0; i < dict.size; i++) { - nlua_push_String(lstate, dict.items[i].key, special); - nlua_push_Object(lstate, &dict.items[i].value, special); + nlua_push_String(lstate, dict.items[i].key, flags); + nlua_push_Object(lstate, &dict.items[i].value, flags); lua_rawset(lstate, -3); } } @@ -727,18 +727,18 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special /// Convert given Array to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Array(lua_State *lstate, const Array array, bool special) +void nlua_push_Array(lua_State *lstate, const Array array, int flags) FUNC_ATTR_NONNULL_ALL { lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, &array.items[i], special); + nlua_push_Object(lstate, &array.items[i], flags); lua_rawseti(lstate, -2, (int)i + 1); } } #define GENERATE_INDEX_FUNCTION(type) \ - void nlua_push_##type(lua_State *lstate, const type item, bool special) \ + void nlua_push_##type(lua_State *lstate, const type item, int flags) \ FUNC_ATTR_NONNULL_ALL \ { \ lua_pushnumber(lstate, (lua_Number)(item)); \ @@ -753,12 +753,12 @@ GENERATE_INDEX_FUNCTION(Tabpage) /// Convert given Object to lua value /// /// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, Object *obj, bool special) +void nlua_push_Object(lua_State *lstate, Object *obj, int flags) FUNC_ATTR_NONNULL_ALL { switch (obj->type) { case kObjectTypeNil: - if (special) { + if (flags & kNluaPushSpecial) { lua_pushnil(lstate); } else { nlua_pushref(lstate, nlua_global_refs->nil_ref); @@ -766,13 +766,15 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special) break; case kObjectTypeLuaRef: { nlua_pushref(lstate, obj->data.luaref); - api_free_luaref(obj->data.luaref); - obj->data.luaref = LUA_NOREF; + if (flags & kNluaPushFreeRefs) { + api_free_luaref(obj->data.luaref); + obj->data.luaref = LUA_NOREF; + } break; } #define ADD_TYPE(type, data_key) \ case kObjectType##type: { \ - nlua_push_##type(lstate, obj->data.data_key, special); \ + nlua_push_##type(lstate, obj->data.data_key, flags); \ break; \ } ADD_TYPE(Boolean, boolean) @@ -784,7 +786,7 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj->data.integer, special); \ + nlua_push_##type(lstate, (type)obj->data.integer, flags); \ break; \ } ADD_REMOTE_TYPE(Buffer) @@ -1380,7 +1382,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { - nlua_push_Object(L, (Object *)mem, false); + nlua_push_Object(L, (Object *)mem, 0); } else if (field->type == kObjectTypeInteger) { lua_pushinteger(L, *(Integer *)mem); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow @@ -1391,11 +1393,11 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) } else if (field->type == kObjectTypeBoolean) { lua_pushboolean(L, *(Boolean *)mem); } else if (field->type == kObjectTypeString) { - nlua_push_String(L, *(String *)mem, false); + nlua_push_String(L, *(String *)mem, 0); } else if (field->type == kObjectTypeArray) { - nlua_push_Array(L, *(Array *)mem, false); + nlua_push_Array(L, *(Array *)mem, 0); } else if (field->type == kObjectTypeDictionary) { - nlua_push_Dictionary(L, *(Dictionary *)mem, false); + nlua_push_Dictionary(L, *(Dictionary *)mem, 0); } else if (field->type == kObjectTypeLuaRef) { nlua_pushref(L, *(LuaRef *)mem); } else { diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index a502df80d9..d1ba61bcee 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -9,6 +9,12 @@ #define nlua_pop_Window nlua_pop_handle #define nlua_pop_Tabpage nlua_pop_handle +/// Flags for nlua_push_*() functions. +enum { + kNluaPushSpecial = 0x01, ///< Use lua-special-tbl when necessary + kNluaPushFreeRefs = 0x02, ///< Free luarefs to elide an api_luarefs_free_*() later +}; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/converter.h.generated.h" #endif diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index cfc68dc08f..9096cac619 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -103,7 +103,7 @@ typedef struct { if (args[i].v_type == VAR_UNKNOWN) { \ lua_pushnil(lstate); \ } else { \ - nlua_push_typval(lstate, &args[i], special); \ + nlua_push_typval(lstate, &args[i], (special) ? kNluaPushSpecial : 0); \ } \ } @@ -325,7 +325,7 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) } ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all); - nlua_push_Array(lstate, ret, true); + nlua_push_Array(lstate, ret, kNluaPushSpecial); api_free_array(ret); api_free_array(pat); @@ -1210,7 +1210,7 @@ int nlua_call(lua_State *lstate) }); if (!ERROR_SET(&err)) { - nlua_push_typval(lstate, &rettv, false); + nlua_push_typval(lstate, &rettv, 0); } tv_clear(&rettv); @@ -1261,7 +1261,7 @@ static int nlua_rpc(lua_State *lstate, bool request) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { - nlua_push_Object(lstate, &result, false); + nlua_push_Object(lstate, &result, 0); arena_mem_free(res_mem); } } else { @@ -1564,7 +1564,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, &args.items[i], false); + nlua_push_Object(lstate, &args.items[i], 0); } if (nlua_pcall(lstate, (int)args.size, 1)) { @@ -1611,7 +1611,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, nargs++; } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, &args.items[i], false); + nlua_push_Object(lstate, &args.items[i], 0); } if (nlua_pcall(lstate, nargs, 1)) { diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index a5262efcfa..788185f2b4 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -449,7 +449,7 @@ int nlua_getvar(lua_State *lstate) if (di == NULL) { return 0; // nil } - nlua_push_typval(lstate, &di->di_tv, false); + nlua_push_typval(lstate, &di->di_tv, 0); return 1; } diff --git a/src/nvim/match.c b/src/nvim/match.c index ea8a1a05f4..580d7d1069 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -100,7 +100,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in // Build new match. matchitem_T *m = xcalloc(1, sizeof(matchitem_T)); - if (pos_list != NULL) { + if (tv_list_len(pos_list) > 0) { m->mit_pos_array = xcalloc((size_t)tv_list_len(pos_list), sizeof(llpos_T)); m->mit_pos_count = tv_list_len(pos_list); } @@ -1103,7 +1103,7 @@ void f_matchaddpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) list_T *l; l = argvars[1].vval.v_list; - if (l == NULL) { + if (tv_list_len(l) == 0) { return; } diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index e5412a8e99..d2ec3a576c 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -355,6 +355,44 @@ describe('autocmd api', function() test({ 'list' }) test({ foo = 'bar' }) end) + + it('function in arbitrary data is passed to all autocmds #28353', function() + eq( + 1303, + exec_lua([[ + local res = 1 + + local fun = function(m, x) + res = res * m + x + end + + local group = vim.api.nvim_create_augroup('MyTest', { clear = false }) + + vim.api.nvim_create_autocmd('User', { + group = group, + callback = function(payload) + payload.data.fun(10, payload.data.x) + end, + pattern = 'MyEvent', + }) + vim.api.nvim_create_autocmd('User', { + group = group, + callback = function(payload) + payload.data.fun(100, payload.data.x) + end, + pattern = 'MyEvent', + }) + + vim.api.nvim_exec_autocmds('User', { + group = group, + pattern = 'MyEvent', + data = { x = 3, fun = fun }, + }) + + return res + ]]) + ) + end) end) describe('nvim_get_autocmds', function() diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 4e9a42f11d..c412773482 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -559,6 +559,16 @@ describe('API', function() eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) + + it('can return Lua function to Lua code', function() + eq( + [["a string with \"double quotes\" and 'single quotes'"]], + exec_lua([=[ + local fun = vim.api.nvim_eval([[luaeval('string.format')]]) + return fun('%q', [[a string with "double quotes" and 'single quotes']]) + ]=]) + ) + end) end) describe('nvim_call_function', function() @@ -624,6 +634,16 @@ describe('API', function() pcall_err(request, 'nvim_call_function', 'Foo', too_many_args) ) end) + + it('can return Lua function to Lua code', function() + eq( + [["a string with \"double quotes\" and 'single quotes'"]], + exec_lua([=[ + local fun = vim.api.nvim_call_function('luaeval', { 'string.format' }) + return fun('%q', [[a string with "double quotes" and 'single quotes']]) + ]=]) + ) + end) end) describe('nvim_call_dict_function', function() diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 649d5f5c6c..0adc3286f9 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -1441,4 +1441,10 @@ func Test_indexof() delfunc TestIdx endfunc +func Test_extendnew_leak() + " This used to leak memory + for i in range(100) | silent! call extendnew([], [], []) | endfor + for i in range(100) | silent! call extendnew({}, {}, {}) | endfor +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_match.vim b/test/old/testdir/test_match.vim index 1cb52b8b2a..ddf032d593 100644 --- a/test/old/testdir/test_match.vim +++ b/test/old/testdir/test_match.vim @@ -310,6 +310,7 @@ func Test_matchaddpos_error() " call assert_fails("call matchaddpos('Error', [{}])", 'E290:') call assert_fails("call matchaddpos('Error', [{}])", 'E5031:') call assert_equal(-1, matchaddpos('Error', v:_null_list)) + call assert_equal(-1, matchaddpos('Error', [])) call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:') endfunc @@ -427,13 +428,11 @@ func Test_match_tab_with_linebreak() call setline(1, "\tix") call matchadd('ErrorMsg', '\t') END - call writefile(lines, 'XscriptMatchTabLinebreak') + call writefile(lines, 'XscriptMatchTabLinebreak', 'D') let buf = RunVimInTerminal('-S XscriptMatchTabLinebreak', #{rows: 10}) call VerifyScreenDump(buf, 'Test_match_tab_linebreak', {}) call StopVimInTerminal(buf) - call delete('XscriptMatchTabLinebreak') endfunc - " vim: shiftwidth=2 sts=2 expandtab |