From 2ea9ed32e4e73b0373afd56945e66e23f0bd3874 Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Thu, 16 Apr 2026 03:59:20 -0400 Subject: [PATCH] fix(treesitter): restore highlighting on 32 bit systems #39091 Problem: Treesitter highlighting regressed on 32-bit builds because ranges that should cover the whole buffer were corrupted when passed into Lua. Solution: Round-trip those range values through Lua and validate them so treesitter sees the same ranges on 32 and 64-bit builds. (cherry picked from commit 3838a2579e850d1071977653fa8a5a71397323be) --- src/nvim/lua/treesitter.c | 34 ++++++++++++++-------- test/functional/treesitter/parser_spec.lua | 17 +++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 7eee8b7022..b02aff64c4 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -476,20 +476,20 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length for (size_t i = 0; i < length; i++) { lua_createtable(L, include_bytes ? 6 : 4, 0); int j = 1; - lua_pushinteger(L, ranges[i].start_point.row); + lua_pushnumber(L, (lua_Number)ranges[i].start_point.row); lua_rawseti(L, -2, j++); - lua_pushinteger(L, ranges[i].start_point.column); + lua_pushnumber(L, (lua_Number)ranges[i].start_point.column); lua_rawseti(L, -2, j++); if (include_bytes) { - lua_pushinteger(L, ranges[i].start_byte); + lua_pushnumber(L, (lua_Number)ranges[i].start_byte); lua_rawseti(L, -2, j++); } - lua_pushinteger(L, ranges[i].end_point.row); + lua_pushnumber(L, (lua_Number)ranges[i].end_point.row); lua_rawseti(L, -2, j++); - lua_pushinteger(L, ranges[i].end_point.column); + lua_pushnumber(L, (lua_Number)ranges[i].end_point.column); lua_rawseti(L, -2, j++); if (include_bytes) { - lua_pushinteger(L, ranges[i].end_byte); + lua_pushnumber(L, (lua_Number)ranges[i].end_byte); lua_rawseti(L, -2, j++); } @@ -596,6 +596,16 @@ static void range_err(lua_State *L) luaL_error(L, "Ranges can only be made from 6 element long tables or nodes."); } +static uint32_t lua_checkuint32(lua_State *L, int index) +{ + lua_Number value = luaL_checknumber(L, index); + uint32_t converted = (uint32_t)value; + if (value < 0 || value > (lua_Number)UINT32_MAX || (lua_Number)converted != value) { + luaL_error(L, "Range value out of bounds"); + } + return converted; +} + // Use the top of the stack (without popping it) to create a TSRange, it can be // either a lua table or a TSNode static void range_from_lua(lua_State *L, TSRange *range) @@ -609,27 +619,27 @@ static void range_from_lua(lua_State *L, TSRange *range) } lua_rawgeti(L, -1, 1); // [ range, start_row] - uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1); + uint32_t start_row = lua_checkuint32(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); // [ range, start_col] - uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1); + uint32_t start_col = lua_checkuint32(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 3); // [ range, start_byte] - uint32_t start_byte = (uint32_t)luaL_checkinteger(L, -1); + uint32_t start_byte = lua_checkuint32(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 4); // [ range, end_row] - uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1); + uint32_t end_row = lua_checkuint32(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 5); // [ range, end_col] - uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1); + uint32_t end_col = lua_checkuint32(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 6); // [ range, end_byte] - uint32_t end_byte = (uint32_t)luaL_checkinteger(L, -1); + uint32_t end_byte = lua_checkuint32(L, -1); lua_pop(L, 1); // [ range ] *range = (TSRange) { diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index bbc0798b82..8678761e6e 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -90,6 +90,23 @@ describe('treesitter parser API', function() eq(true, exec_lua('return parser:parse()[1] == tree2')) end) + it('preserves full-document included ranges', function() + insert([[ + local _ = 1 + ]]) + + eq( + { + { 0, 0, 0, 4294967295, 4294967295, 4294967295 }, + }, + exec_lua(function() + local parser = vim.treesitter.get_parser(0, 'lua') + local tree = parser:parse()[1] + return tree:included_ranges(true) + end) + ) + end) + it('respects eol settings when parsing buffer', function() insert([[ int main() {