diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index e1f4aca74d..03f472dd51 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -399,10 +399,10 @@ static int tslua_push_parser(lua_State *L) return 1; } -static TSParser *parser_check(lua_State *L, uint16_t index) +static TSParser *parser_check(lua_State *L, int index) { TSParser **ud = luaL_checkudata(L, index, TS_META_PARSER); - luaL_argcheck(L, *ud, index, "TSParser expected"); + luaL_argcheck(L, *ud != NULL, index, "Parser has been deleted"); return *ud; } @@ -419,9 +419,12 @@ static void logger_gc(TSLogger logger) static int parser_gc(lua_State *L) { - TSParser *p = parser_check(L, 1); - logger_gc(ts_parser_logger(p)); - ts_parser_delete(p); + TSParser **ud = luaL_checkudata(L, 1, TS_META_PARSER); + if (*ud) { + logger_gc(ts_parser_logger(*ud)); + ts_parser_delete(*ud); + *ud = NULL; + } return 0; } diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index e8f4fe5e57..56182590c6 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -8,6 +8,8 @@ local eq = t.eq local insert = n.insert local exec_lua = n.exec_lua local feed = n.feed +local matches = t.matches +local pcall_err = t.pcall_err local run_query = ts_t.run_query local assert_alive = n.assert_alive @@ -107,6 +109,24 @@ describe('treesitter parser API', function() ) end) + it('ignores repeated parser finalization', function() + matches( + 'Parser has been deleted', + pcall_err(exec_lua, function() + vim.treesitter.language.add('c') + + local parser = vim._create_ts_parser('c') + -- The parser metatable exposes __gc through __index, so this simulates a finalizer + -- running before all Lua references to the userdata are gone. + parser:__gc() + parser:__gc() + + parser:parse(nil, 'int x;', false) + end) + ) + assert_alive() + end) + it('respects eol settings when parsing buffer', function() insert([[ int main() {