neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 7f33c1967b78ca8fda11fb0ad4c7f57d563e6ede
parent 12c9791e0fef7ee0d6cf6d3b828caa488d6347ea
Author: Tyler Miller <tmillr@proton.me>
Date:   Wed,  3 Jul 2024 15:36:00 -0700

fix(lua): use rawget() to get __call in vim.is_callable() (#29536)

Lua 5.1 uses a "raw get" to retrieve `__call` from a metatable to
determine if a table is callable. Mirror this behavior in
`vim.is_callable()`.
Diffstat:
Mruntime/lua/vim/shared.lua | 2+-
Mtest/functional/lua/vim_spec.lua | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua @@ -997,7 +997,7 @@ function vim.is_callable(f) if m == nil then return false end - return type(m.__call) == 'function' + return type(rawget(m, '__call')) == 'function' end --- Creates a table whose missing keys are provided by {createfn} (like Python's "defaultdict"). diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua @@ -1495,6 +1495,60 @@ describe('lua stdlib', function() ]]) ) + eq( + { false, false }, + exec_lua([[ + local meta = { __call = {} } + assert(meta.__call) + local function new() + return setmetatable({}, meta) + end + local not_callable = new() + return { pcall(function() not_callable() end), vim.is_callable(not_callable) } + ]]) + ) + eq( + { false, false }, + exec_lua([[ + local function new() + return { __call = function()end } + end + local not_callable = new() + assert(not_callable.__call) + return { pcall(function() not_callable() end), vim.is_callable(not_callable) } + ]]) + ) + eq( + { false, false }, + exec_lua([[ + local meta = setmetatable( + { __index = { __call = function() end } }, + { __index = { __call = function() end } } + ) + assert(meta.__call) + local not_callable = setmetatable({}, meta) + assert(not_callable.__call) + return { pcall(function() not_callable() end), vim.is_callable(not_callable) } + ]]) + ) + eq( + { false, false }, + exec_lua([[ + local meta = setmetatable({ + __index = function() + return function() end + end, + }, { + __index = function() + return function() end + end, + }) + assert(meta.__call) + local not_callable = setmetatable({}, meta) + assert(not_callable.__call) + return { pcall(function() not_callable() end), vim.is_callable(not_callable) } + ]]) + ) eq(false, exec_lua('return vim.is_callable(1)')) eq(false, exec_lua("return vim.is_callable('foo')")) eq(false, exec_lua('return vim.is_callable({})'))