mirror of https://github.com/rusefi/lua.git
Better error messages for calling non-callable objects
When available, use the calling code to find a suitable name for what was being called; this is particularly useful for errors of non-callable metamethods. This commit also improved the debug information for order metamethods.
This commit is contained in:
parent
59e565d955
commit
4bd10b6fe8
23
ldebug.c
23
ldebug.c
|
@ -629,12 +629,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||||
case OP_LEN: tm = TM_LEN; break;
|
case OP_LEN: tm = TM_LEN; break;
|
||||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||||
case OP_EQ: tm = TM_EQ; break;
|
case OP_EQ: tm = TM_EQ; break;
|
||||||
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
|
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
||||||
*name = "order"; /* '<=' can call '__lt', etc. */
|
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
||||||
return "metamethod";
|
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
||||||
case OP_CLOSE: case OP_RETURN:
|
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
||||||
*name = "close";
|
|
||||||
return "metamethod";
|
|
||||||
default:
|
default:
|
||||||
return NULL; /* cannot find a reasonable name */
|
return NULL; /* cannot find a reasonable name */
|
||||||
}
|
}
|
||||||
|
@ -697,6 +695,19 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
const char *name = NULL; /* to avoid warnings */
|
||||||
|
const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL;
|
||||||
|
if (what != NULL) {
|
||||||
|
const char *t = luaT_objtypename(L, o);
|
||||||
|
luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
luaG_typeerror(L, o, "call");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
||||||
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
||||||
what, luaT_objtypename(L, o));
|
what, luaT_objtypename(L, o));
|
||||||
|
|
1
ldebug.h
1
ldebug.h
|
@ -31,6 +31,7 @@ LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||||
StkId *pos);
|
StkId *pos);
|
||||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||||
const char *opname);
|
const char *opname);
|
||||||
|
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
|
||||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||||
const char *what);
|
const char *what);
|
||||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||||
|
|
2
ldo.c
2
ldo.c
|
@ -372,7 +372,7 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||||
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
||||||
StkId p;
|
StkId p;
|
||||||
if (unlikely(ttisnil(tm)))
|
if (unlikely(ttisnil(tm)))
|
||||||
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
|
luaG_callerror(L, s2v(func)); /* nothing to call */
|
||||||
for (p = L->top; p > func; p--) /* open space for metamethod */
|
for (p = L->top; p > func; p--) /* open space for metamethod */
|
||||||
setobjs2s(L, p, p-1);
|
setobjs2s(L, p, p-1);
|
||||||
L->top++; /* stack space pre-allocated by the caller */
|
L->top++; /* stack space pre-allocated by the caller */
|
||||||
|
|
|
@ -823,8 +823,10 @@ assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
|
||||||
-a == "unm" and #a == "len" and a & 3 == "band")
|
-a == "unm" and #a == "len" and a & 3 == "band")
|
||||||
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
|
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
|
||||||
assert (a==b and a.op == "eq")
|
assert (a==b and a.op == "eq")
|
||||||
assert (a>=b and a.op == "order")
|
assert (a>=b and a.op == "le")
|
||||||
assert (a>b and a.op == "order")
|
assert ("x">=a and a.op == "le")
|
||||||
|
assert (a>b and a.op == "lt")
|
||||||
|
assert (a>10 and a.op == "lt")
|
||||||
assert(~a == "bnot")
|
assert(~a == "bnot")
|
||||||
|
|
||||||
do -- testing for-iterator name
|
do -- testing for-iterator name
|
||||||
|
|
|
@ -24,8 +24,9 @@ local function doit (s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function checkmessage (prog, msg)
|
local function checkmessage (prog, msg, debug)
|
||||||
local m = doit(prog)
|
local m = doit(prog)
|
||||||
|
if debug then print(m) end
|
||||||
assert(string.find(m, msg, 1, true))
|
assert(string.find(m, msg, 1, true))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,6 +121,17 @@ assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
|
||||||
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
|
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
|
||||||
checkmessage("a=(1)..{}", "a table value")
|
checkmessage("a=(1)..{}", "a table value")
|
||||||
|
|
||||||
|
-- calls
|
||||||
|
checkmessage("local a; a(13)", "local 'a'")
|
||||||
|
checkmessage([[
|
||||||
|
local a = setmetatable({}, {__add = 34})
|
||||||
|
a = a + 1
|
||||||
|
]], "metamethod 'add'")
|
||||||
|
checkmessage([[
|
||||||
|
local a = setmetatable({}, {__lt = {}})
|
||||||
|
a = a > a
|
||||||
|
]], "metamethod 'lt'")
|
||||||
|
|
||||||
-- tail calls
|
-- tail calls
|
||||||
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
||||||
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
||||||
|
|
|
@ -459,7 +459,22 @@ do -- errors due to non-closable values
|
||||||
getmetatable(xyz).__close = nil -- remove metamethod
|
getmetatable(xyz).__close = nil -- remove metamethod
|
||||||
end
|
end
|
||||||
local stat, msg = pcall(foo)
|
local stat, msg = pcall(foo)
|
||||||
assert(not stat and string.find(msg, "attempt to call a nil value"))
|
assert(not stat and string.find(msg, "metamethod 'close'"))
|
||||||
|
|
||||||
|
local function foo ()
|
||||||
|
local a1 <close> = func2close(function (_, msg)
|
||||||
|
assert(string.find(msg, "number value"))
|
||||||
|
error(12)
|
||||||
|
end)
|
||||||
|
local a2 <close> = setmetatable({}, {__close = print})
|
||||||
|
local a3 <close> = func2close(function (_, msg)
|
||||||
|
assert(msg == nil)
|
||||||
|
error(123)
|
||||||
|
end)
|
||||||
|
getmetatable(a2).__close = 4 -- invalidate metamethod
|
||||||
|
end
|
||||||
|
local stat, msg = pcall(foo)
|
||||||
|
assert(not stat and msg == 12)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue