diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 4e017d23be..ef0d6d0dc9 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -277,16 +277,18 @@ lua_State *get_global_lstate(void) /// The returned string points to memory on the Lua stack. Use or duplicate it before using /// `lstate` again. /// -/// @param[out] len length of error (can be NULL) +/// @param[out] len length of error static const char *nlua_get_error(lua_State *lstate, size_t *len) + FUNC_ATTR_NONNULL_RET { - if (luaL_getmetafield(lstate, -1, "__tostring")) { - if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) { - // call __tostring, convert the result and replace error with it - lua_replace(lstate, -3); + if (lua_type(lstate, -1) != LUA_TSTRING) { + lua_getglobal(lstate, "tostring"); + lua_pushvalue(lstate, -2); + if (lua_pcall(lstate, 1, 1, 0) || lua_type(lstate, -1) != LUA_TSTRING) { + lua_pop(lstate, 1); + lua_pushstring(lstate, "[UNPRINTABLE ERROR]"); } - // pop __tostring. - lua_pop(lstate, 1); + lua_replace(lstate, -2); } return lua_tolstring(lstate, -1, len); @@ -433,10 +435,11 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres pthread_exit(0); #endif } - const char *error = lua_tostring(lstate, -1); + size_t len; + const char *error = nlua_get_error(lstate, &len); loop_schedule_deferred(&main_loop, event_create(nlua_luv_error_event, - error != NULL ? xstrdup(error) : NULL, + xmemdupz(error, len), (void *)(intptr_t)(is_callback ? kThreadCallback : kThread))); diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index d5a83015bc..c1eb1f92c3 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -828,7 +828,7 @@ describe('nvim_create_user_command', function() }) ]]) feed(':Test ') - eq('E5108: Lua function: [NULL]', api.nvim_get_vvar('errmsg')) + eq('E5108: Lua function: nil', api.nvim_get_vvar('errmsg')) eq('Test ', fn.getcmdline()) assert_alive() end) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 0bfb9f006f..70ac8f2e7c 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -72,8 +72,22 @@ describe(':lua', function() eq({ '' }, api.nvim_buf_get_lines(0, 0, 100, false)) end) - it('works with NULL errors', function() - eq([=[Vim(lua):E5108: Lua: [NULL]]=], pcall_err(command, 'lua error(nil)')) + it('works with nil errors', function() + eq([=[Vim(lua):E5108: Lua: nil]=], pcall_err(command, 'lua error(nil)')) + end) + + it('works with errors with __tostring failure', function() + eq( + [=[Vim(lua):E5108: Lua: [UNPRINTABLE ERROR]]=], + pcall_err(command, 'lua error(setmetatable({}, {__tostring=error}))') + ) + end) + + it('works with printable errors', function() + eq([=[Vim(lua):E5108: Lua: false]=], pcall_err(command, 'lua error(false)')) + -- numbers get the location prefixed because numbers are almost strings (know the workplace rules) + eq([=[Vim(lua):E5108: Lua: [string ":lua"]:0: 39]=], pcall_err(command, 'lua error(39)')) + matches([=[Vim%(lua%):E5108: Lua: table: 0x%x+]=], pcall_err(command, 'lua error({})')) end) it('accepts embedded NLs without heredoc', function() @@ -280,8 +294,8 @@ describe(':luado command', function() ) end) - it('works with NULL errors', function() - eq([=[Vim(luado):E5111: Lua: [NULL]]=], pcall_err(command, 'luado error(nil)')) + it('works with nil errors', function() + eq([=[Vim(luado):E5111: Lua: nil]=], pcall_err(command, 'luado error(nil)')) end) it('fails in sandbox when needed', function() @@ -342,8 +356,8 @@ describe(':luafile', function() ) end) - it('works with NULL errors', function() + it('works with nil errors', function() write_file(fname, 'error(nil)') - eq([=[Vim(luafile):E5113: Lua chunk: [NULL]]=], pcall_err(command, 'luafile ' .. fname)) + eq([=[Vim(luafile):E5113: Lua chunk: nil]=], pcall_err(command, 'luafile ' .. fname)) end) end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 1eb8cf9958..e4591a3a60 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -495,7 +495,7 @@ describe('luaeval()', function() remove_trace(pcall_err(command, [[call luaeval("error('ERROR')")]])) ) eq( - 'Vim(call):E5108: Lua: [NULL]', + 'Vim(call):E5108: Lua: nil', remove_trace(pcall_err(command, [[call luaeval("error(nil)")]])) ) end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index e0d0bd908e..def1f7ae73 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -4,6 +4,7 @@ local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local eq = t.eq +local matches = t.matches local NIL = vim.NIL local feed = n.feed local clear = n.clear @@ -105,13 +106,13 @@ describe('print', function() 'Vim(lua):E5108: Lua: Xtest-functional-lua-overrides-luafile:2: 1234', pcall_err(command, 'lua number_error()') ) - eq('Vim(lua):E5108: Lua: [NULL]', pcall_err(command, 'lua nil_error()')) - eq('Vim(lua):E5108: Lua: [NULL]', pcall_err(command, 'lua table_error()')) + eq('Vim(lua):E5108: Lua: nil', pcall_err(command, 'lua nil_error()')) + matches('^Vim%(lua%):E5108: Lua: table: 0x%x+$', pcall_err(command, 'lua table_error()')) eq( 'Vim(lua):E5108: Lua: Internal Error [11234] my mistake', pcall_err(command, 'lua custom_error()') ) - eq('Vim(lua):E5108: Lua: [NULL]', pcall_err(command, 'lua bad_custom_error()')) + eq('Vim(lua):E5108: Lua: [UNPRINTABLE ERROR]', pcall_err(command, 'lua bad_custom_error()')) end) it('prints strings with NULs and NLs correctly', function() api.nvim_set_option_value('more', true, {}) diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua index e4ff2b8068..1232b3843d 100644 --- a/test/functional/lua/thread_spec.lua +++ b/test/functional/lua/thread_spec.lua @@ -32,7 +32,7 @@ describe('thread', function() {1:~ }|*5 {3: }| {9:Luv thread:} | - {9:[NULL]} | + {9:nil} | {6:Press ENTER or type command to continue}^ | ]]) feed('') diff --git a/test/functional/lua/uv_spec.lua b/test/functional/lua/uv_spec.lua index 848a073cb5..7c865da54f 100644 --- a/test/functional/lua/uv_spec.lua +++ b/test/functional/lua/uv_spec.lua @@ -174,7 +174,7 @@ describe('vim.uv', function() {1:~ }|*5 {3: }| {9:Lua callback:} | - {9:[NULL]} | + {9:nil} | {6:Press ENTER or type command to continue}^ | ]] screen:expect(s) @@ -209,7 +209,7 @@ describe('vim.uv', function() screen:expect([[ {3: }| {9:Lua callback:} | - {9:[NULL]} | + {9:nil} | {6:Press ENTER or type command to continue}^ | ]]) feed('')