use omnilua::{Lua, LuaVersion};
struct Golden {
version: LuaVersion,
snippet: &'static str,
expected: &'static str,
}
const fn g(version: LuaVersion, snippet: &'static str, expected: &'static str) -> Golden {
Golden {
version,
snippet,
expected,
}
}
const GOLDEN: &[Golden] = &[
g(
LuaVersion::V51,
"coroutine.resume, \"x\"",
"bad argument #1 to '?' (coroutine expected)",
),
g(
LuaVersion::V51,
"math.max, \"hello\"",
"bad argument #1 to '?' (number expected, got string)",
),
g(
LuaVersion::V51,
"string.format, \"%d\", \"x\"",
"bad argument #2 to '?' (number expected, got string)",
),
g(
LuaVersion::V51,
"string.rep",
"bad argument #1 to '?' (string expected, got no value)",
),
g(
LuaVersion::V51,
"table.insert, 1, 2",
"bad argument #1 to '?' (table expected, got number)",
),
g(
LuaVersion::V51,
"setmetatable, 1, 2",
"bad argument #1 to '?' (table expected, got number)",
),
g(
LuaVersion::V51,
"ipairs",
"bad argument #1 to '?' (table expected, got no value)",
),
g(
LuaVersion::V51,
"next, 1",
"bad argument #1 to '?' (table expected, got number)",
),
g(
LuaVersion::V51,
"select, \"x\"",
"bad argument #1 to '?' (number expected, got string)",
),
g(
LuaVersion::V52,
"coroutine.resume, \"x\"",
"bad argument #1 to 'coroutine.resume' (coroutine expected)",
),
g(
LuaVersion::V52,
"math.max, \"hello\"",
"bad argument #1 to 'math.max' (number expected, got string)",
),
g(
LuaVersion::V52,
"string.format, \"%d\", \"x\"",
"bad argument #2 to 'string.format' (number expected, got string)",
),
g(
LuaVersion::V52,
"table.insert, 1, 2",
"bad argument #1 to 'table.insert' (table expected, got number)",
),
g(
LuaVersion::V52,
"setmetatable, 1, 2",
"bad argument #1 to '_G.setmetatable' (table expected, got number)",
),
g(
LuaVersion::V52,
"ipairs",
"bad argument #1 to '_G.ipairs' (table expected, got no value)",
),
g(
LuaVersion::V52,
"next, 1",
"bad argument #1 to '_G.next' (table expected, got number)",
),
g(
LuaVersion::V52,
"tonumber, \"x\", 99",
"bad argument #2 to '_G.tonumber' (base out of range)",
),
g(
LuaVersion::V52,
"select, \"x\"",
"bad argument #1 to '_G.select' (number expected, got string)",
),
g(
LuaVersion::V53,
"coroutine.resume, \"x\"",
"bad argument #1 to 'coroutine.resume' (thread expected)",
),
g(
LuaVersion::V53,
"string.format, \"%d\", \"x\"",
"bad argument #2 to 'string.format' (number expected, got string)",
),
g(
LuaVersion::V53,
"table.insert, 1, 2",
"bad argument #1 to 'table.insert' (table expected, got number)",
),
g(
LuaVersion::V53,
"setmetatable, 1, 2",
"bad argument #1 to 'setmetatable' (table expected, got number)",
),
g(
LuaVersion::V53,
"ipairs",
"bad argument #1 to 'ipairs' (value expected)",
),
g(
LuaVersion::V53,
"next, 1",
"bad argument #1 to 'next' (table expected, got number)",
),
g(
LuaVersion::V53,
"select, \"x\"",
"bad argument #1 to 'select' (number expected, got string)",
),
g(
LuaVersion::V54,
"coroutine.resume, \"x\"",
"bad argument #1 to 'coroutine.resume' (thread expected, got string)",
),
g(
LuaVersion::V54,
"string.format, \"%d\", \"x\"",
"bad argument #2 to 'string.format' (number expected, got string)",
),
g(
LuaVersion::V54,
"table.insert, 1, 2",
"bad argument #1 to 'table.insert' (table expected, got number)",
),
g(
LuaVersion::V54,
"ipairs",
"bad argument #1 to 'ipairs' (value expected)",
),
g(
LuaVersion::V54,
"next, 1",
"bad argument #1 to 'next' (table expected, got number)",
),
g(
LuaVersion::V54,
"select, \"x\"",
"bad argument #1 to 'select' (number expected, got string)",
),
g(
LuaVersion::V55,
"coroutine.resume, \"x\"",
"bad argument #1 to 'coroutine.resume' (thread expected, got string)",
),
g(
LuaVersion::V55,
"string.format, \"%d\", \"x\"",
"bad argument #2 to 'string.format' (number expected, got string)",
),
g(
LuaVersion::V55,
"table.insert, 1, 2",
"bad argument #1 to 'table.insert' (table expected, got number)",
),
g(
LuaVersion::V55,
"ipairs",
"bad argument #1 to 'ipairs' (value expected)",
),
g(
LuaVersion::V55,
"next, 1",
"bad argument #1 to 'next' (table expected, got number)",
),
g(
LuaVersion::V55,
"select, \"x\"",
"bad argument #1 to 'select' (number expected, got string)",
),
];
fn pcall_message(version: LuaVersion, snippet: &str) -> String {
let lua = Lua::new_versioned(version);
let wrapper = format!(
"local ok, msg = pcall({snippet})\n\
if ok then error('expected an error, got success') end\n\
return tostring(msg)"
);
lua.load(&wrapper)
.eval::<String>()
.unwrap_or_else(|e| panic!("error_wording_kit harness failure ({version:?}): {e:?}"))
}
#[test]
fn arg_error_funcname_matches_reference_golden() {
let mut failures = Vec::new();
for row in GOLDEN {
let got = pcall_message(row.version, row.snippet);
if got != row.expected {
failures.push(format!(
" {:?} pcall({})\n ours: {got}\n ref : {}",
row.version, row.snippet, row.expected
));
}
}
assert!(
failures.is_empty(),
"C-function arg-error funcname diverges from reference:\n{}",
failures.join("\n")
);
}
#[test]
fn v51_c_functions_are_question_mark() {
for snippet in &[
"coroutine.resume, \"x\"",
"math.max, \"hello\"",
"string.format, \"%d\", \"x\"",
"string.rep",
"table.insert, 1, 2",
"setmetatable, 1, 2",
"ipairs",
"next, 1",
"select, \"x\"",
] {
let got = pcall_message(LuaVersion::V51, snippet);
assert!(
got.contains("to '?'"),
"5.1 pcall({snippet}) should name the C function '?', got: {got}"
);
}
}
#[test]
fn modern_baselines_keep_qualified_names() {
for version in &[LuaVersion::V54, LuaVersion::V55] {
let got = pcall_message(*version, "coroutine.resume, \"x\"");
assert_eq!(
got, "bad argument #1 to 'coroutine.resume' (thread expected, got string)",
"{version:?} must keep the qualified C-function name"
);
}
}