Skip to content

Commit 8044a10

Browse files
committed
fix: add loadfile to Lua eval-detection guard in both engines (#2043)
Greptile flagged that loadfile is listed in LUA_BUILTIN_GLOBALS alongside load/loadstring/dofile and performs the same dynamic-code-loading operation, but was missing from the eval-detection guard in both the WASM extractor (src/extractors/lua.ts) and the native Rust extractor (crates/codegraph-core/src/extractors/lua.rs) — so loadfile("script.lua")() was not flagged as a dynamic eval site on either engine. Since this PR's purpose is eliminating WASM/native divergence for Lua eval/computed-key detection, fixing loadfile on only one engine would reintroduce exactly the kind of parity gap this PR exists to close. Adds loadfile to the guard on both sides plus a unit test on each (mirroring the existing load/loadstring/dofile coverage). Impact: 1 functions changed, 2 affected
1 parent 3df0ac4 commit 8044a10

3 files changed

Lines changed: 22 additions & 6 deletions

File tree

crates/codegraph-core/src/extractors/lua.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,11 @@ fn handle_lua_function_call(node: &Node, source: &[u8], symbols: &mut FileSymbol
156156
None => return,
157157
};
158158

159-
// load(chunk) / loadstring(chunk) / dofile — dynamic code execution; always undecidable
159+
// load(chunk) / loadstring(chunk) / loadfile(...) / dofile — dynamic code
160+
// execution; always undecidable
160161
if name_node.kind() == "identifier" {
161162
let ident = node_text(&name_node, source);
162-
if matches!(ident, "load" | "loadstring" | "dofile") {
163+
if matches!(ident, "load" | "loadstring" | "loadfile" | "dofile") {
163164
symbols.calls.push(Call {
164165
name: "<dynamic:eval>".to_string(),
165166
line: start_line(node),
@@ -386,6 +387,14 @@ mod tests {
386387
&& c.dynamic_kind.as_deref() == Some("eval")));
387388
}
388389

390+
#[test]
391+
fn classifies_loadfile_call_as_dynamic_eval() {
392+
let s = parse_lua("loadfile(\"script.lua\")()");
393+
assert!(s.calls.iter().any(|c| c.name == "<dynamic:eval>"
394+
&& c.dynamic == Some(true)
395+
&& c.dynamic_kind.as_deref() == Some("eval")));
396+
}
397+
389398
#[test]
390399
fn resolves_bracket_index_call_with_string_literal_key_directly() {
391400
let s = parse_lua("t[\"handler\"]()");

src/extractors/lua.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,12 @@ function handleLuaFunctionCall(node: TreeSitterNode, ctx: ExtractorOutput): void
258258
const nameNode = node.childForFieldName('name');
259259
if (!nameNode) return;
260260

261-
// load(chunk) / loadstring(chunk) / dofile(...) — dynamic code execution;
262-
// undecidable statically. Mirrors handle_lua_function_call's `load` arm in
263-
// crates/codegraph-core/src/extractors/lua.rs.
261+
// load(chunk) / loadstring(chunk) / loadfile(...) / dofile(...) — dynamic
262+
// code execution; undecidable statically. Mirrors handle_lua_function_call's
263+
// `load` arm in crates/codegraph-core/src/extractors/lua.rs.
264264
if (nameNode.type === 'identifier') {
265265
const ident = nameNode.text;
266-
if (ident === 'load' || ident === 'loadstring' || ident === 'dofile') {
266+
if (ident === 'load' || ident === 'loadstring' || ident === 'loadfile' || ident === 'dofile') {
267267
ctx.calls.push({
268268
name: '<dynamic:eval>',
269269
line: node.startPosition.row + 1,

tests/parsers/lua.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ string.format("%s", name)`);
164164
);
165165
});
166166

167+
it('classifies loadfile(...) as a dynamic eval call', () => {
168+
const symbols = parseLua(`loadfile("script.lua")()`);
169+
expect(symbols.calls).toContainEqual(
170+
expect.objectContaining({ name: '<dynamic:eval>', dynamic: true, dynamicKind: 'eval' }),
171+
);
172+
});
173+
167174
it('classifies dofile(...) as a dynamic eval call', () => {
168175
const symbols = parseLua(`dofile("script.lua")`);
169176
expect(symbols.calls).toContainEqual(

0 commit comments

Comments
 (0)