fix(extract): emit imports edge for bare Lua require() statements (#3320) - #3404
fix(extract): emit imports edge for bare Lua require() statements (#3320)#3404ousamabenyounes wants to merge 1 commit into
Conversation
…aphify-Labs#3320) A bare `require("mod")` statement with no assignment — the common Neovim/LazyVim `init.lua` idiom — produced no `imports` edge because _LUA_CONFIG.import_types only matched `variable_declaration`. Dispatch bare `function_call` requires to _import_lua as well, guarding on the callee so `myrequire(...)` and string literals containing `require(...)` do not mint false edges. The assigned form stays a single edge (the engine returns after handling the variable_declaration import node, so its nested function_call is not re-dispatched).
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Emits an imports edge for bare require("mod") Lua statements (the Neovim/LazyVim init.lua idiom), which parse as top-level function_call nodes rather than variable_declaration and previously produced no edge. Guards against false positives in _import_lua by checking the callee is exactly require, so myrequire(...) and string literals containing require(...) are ignored, and relies on the engine not re-visiting the nested call of an assigned local x = require(...) to avoid duplicate edges.
No blocking issues surfaced. 6 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2052 functions depend on the 448 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 564 callers, 43 callees - new:
_rebuild_code()— 115 callers, 51 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
_get_extractor()— 26 callers, 6 callees - new:
run_pipeline()— 8 callers, 13 callees - new:
collect_files()— 17 callers, 6 callees - …and 28 more — each is listed as a finding
Verification — 2052 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1887 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_import\_lua.
The verifier did not have enough to check \_import\_lua, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
· 36 more finding(s) on lines outside this diff (see the check run).
Why
graphify.extract._import_luaonly extracted animportsedge when arequire(...)call appeared inside avariable_declaration(local x = require("mod")). The far more common Neovim/LazyVim idiom — a barerequire("mod")statement with no assignment — produced no edge at all:A bare
require(...)parses as a top-levelfunction_call, which was not in_LUA_CONFIG.import_types, so the import handler never fired. Result: real Neovim configs graphed with almost no structure (e.g. a 14-file config produced 13 nodes but only 2containsedges, zero imports).What changed
function_callto_LUA_CONFIG.import_typesso barerequire(...)statements are dispatched to_import_lua(graphify/extract.py)._import_lua: for afunction_callnode it only proceeds when the callee is therequireglobal (ASTnamefield), somyrequire(...), arequire-containing string literal (print("require(x)")), or a method call (obj:require(...)) do not mint a false import edge.local x = require(...)) is unchanged: it is avariable_declaration, and the engine returns after handling an import node, so its nestedfunction_callis never re-dispatched — no duplicate edge.walk_callspass and are unaffected. The change is scoped to_LUA_CONFIG(Lua/.luau/.toconly).Why it's safe
Structure extraction in Lua only walks classes (none),
function_declaration(a statement, never an expression argument), and imports — none of which live inside afunction_call's arguments. Verified empirically that base vs. branch output is byte-identical forprint(require("x")),require("lazy").setup({}), and callback-argument cases; the only behavioral difference is the intended bare-requireimport edge.ruff check .— cleanpython -m tools.skillgen --check— OK (no generated skill files touched)Test verification (RED → GREEN)
New test file
tests/test_lua_import_resolution.py.RED — on unmodified
v8(fix reverted), the bare-require tests fail:GREEN — with the fix:
The
test_similarly_named_call_does_not_emit_importandtest_assigned_require_still_emits_exactly_one_edgecases lock in the no-false-positive and no-duplicate guarantees.Fixes #3320