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:
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({})'))