Skip to content

fix(extract): emit imports edge for bare Lua require() statements (#3320) - #3404

Open
ousamabenyounes wants to merge 1 commit into
Graphify-Labs:v8from
ousamabenyounes:fix/issue-3320-lua-bare-require
Open

fix(extract): emit imports edge for bare Lua require() statements (#3320)#3404
ousamabenyounes wants to merge 1 commit into
Graphify-Labs:v8from
ousamabenyounes:fix/issue-3320-lua-bare-require

Conversation

@ousamabenyounes

Copy link
Copy Markdown
Contributor

Why

graphify.extract._import_lua only extracted an imports edge when a require(...) call appeared inside a variable_declaration (local x = require("mod")). The far more common Neovim/LazyVim idiom — a bare require("mod") statement with no assignment — produced no edge at all:

-- init.lua
require("config.lazy")
require("config.options")
require("config.keymaps")

A bare require(...) parses as a top-level function_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 2 contains edges, zero imports).

What changed

  • Add function_call to _LUA_CONFIG.import_types so bare require(...) statements are dispatched to _import_lua (graphify/extract.py).
  • Guard the new path in _import_lua: for a function_call node it only proceeds when the callee is the require global (AST name field), so myrequire(...), a require-containing string literal (print("require(x)")), or a method call (obj:require(...)) do not mint a false import edge.
  • The assigned form (local x = require(...)) is unchanged: it is a variable_declaration, and the engine returns after handling an import node, so its nested function_call is never re-dispatched — no duplicate edge.
  • Call edges come from the separate walk_calls pass and are unaffected. The change is scoped to _LUA_CONFIG (Lua/.luau/.toc only).

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 a function_call's arguments. Verified empirically that base vs. branch output is byte-identical for print(require("x")), require("lazy").setup({}), and callback-argument cases; the only behavioral difference is the intended bare-require import edge.

  • ruff check . — clean
  • python -m tools.skillgen --check — OK (no generated skill files touched)
  • Full suite: 5479 passed, 14 skipped, 0 failed (baseline v8 was 5475 passed, 0 failed; +4 new Lua tests)

Test verification (RED → GREEN)

New test file tests/test_lua_import_resolution.py.

RED — on unmodified v8 (fix reverted), the bare-require tests fail:

FAILED tests/test_lua_import_resolution.py::test_bare_require_statement_emits_import_edge
FAILED tests/test_lua_import_resolution.py::test_multiple_bare_requires_each_emit_an_edge
2 failed, 1 passed

GREEN — with the fix:

tests/test_lua_import_resolution.py ....                                 [100%]
4 passed in 0.29s

The test_similarly_named_call_does_not_emit_import and test_assigned_require_still_emits_exactly_one_edge cases lock in the no-false-positive and no-duplicate guarantees.

Fixes #3320

…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).

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lua extractor misses bare require() statements (no assignment)

1 participant