fix(elixir): extract functions whose only clause has a when guard (#3111) - #3118
fix(elixir): extract functions whose only clause has a when guard (#3111)#3118santhiprakash wants to merge 1 commit into
Conversation
- Problem: tree-sitter-elixir wraps `def f(x) when guard` in a binary_operator, so a function whose only clause is guarded was dropped from the graph. - Fix: unwrap binary_operator heads before reading the function name so single-clause guarded `def`/`defp` create nodes like unguarded clauses. - Verification: `uv run --frozen pytest tests/test_languages.py -k elixir -q` — 9 passed. Fixes Graphify-Labs#3111
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
Fixes the Elixir extractor dropping single-clause functions whose only head carries a when guard: extract_elixir now unwraps the binary_operator node tree-sitter wraps a guarded head in, walking down to the underlying call/identifier so guarded def/defp get nodes like any other. Adds test_elixir_guarded_single_clause_is_extracted covering plain, guarded, mixed multi-clause, and guarded-private cases.
No blocking issues surfaced.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 536 functions depend on the 536 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract_elixir()— 11 callers, 5 callees
Verification — 536 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: 536 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify extract\_elixir.
The verifier did not have enough to check extract\_elixir, 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: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 1 more finding(s) on lines outside this diff (see the check run).
Summary
The Elixir AST extractor silently drops every function whose only clause carries a
whenguard. On a real 2,900-file Elixir/Phoenix codebase this hid 19 of 582 functions (every function of that shape) from the graph. No error is raised; the node is never created.Problem
tree-sitter-elixir wraps a guarded head in a
binary_operator:def plain(x) do ...→argumentschildren =['call']def guarded(x) when is_integer(x) do ...→argumentschildren =['binary_operator']The extractor only looked for a direct
call/identifierchild ofarguments, sofunc_namestayedNoneand the function was dropped. Multi-clause functions survived via an unguarded clause, which is why this was easy to miss.Fix
Unwrap
binary_operator(thewhenguard) to the left-hand head before reading the function name. Scoped todef/defpname extraction; the rest of the walker is unchanged.This is independent of open #2970 (single-line
def f(x), do: ...call-graph bodies). That PR does not unwrap guarded heads.Verification
9 passed, including
test_elixir_guarded_single_clause_is_extractedcovering:whenguarddefp, single clause,whenguardReproduction (before fix)
extract_elixirproducedDemoanddemo.exonly — noguardednode.Fixes #3111