feat(elixir): index Elixir with Phoenix, Plug and Ecto flow coverage - #1648
Open
ferrine wants to merge 1 commit into
Open
feat(elixir): index Elixir with Phoenix, Plug and Ecto flow coverage#1648ferrine wants to merge 1 commit into
ferrine wants to merge 1 commit into
Conversation
Elixir's grammar has no declaration node types — `defmodule`, `def`,
`alias`, an Ecto `schema` and a Phoenix route are all the same `call`
node, told apart only by the text of the target identifier. So the
generic node-type ladder has nothing to match and every construct is
dispatched through the visitNode hook, which also owns call extraction
(visitFunctionBody does not invoke the hook, and Elixir call sites live
inside bodies).
Three things beyond plain symbol extraction:
- Alias expansion — `alias Foo.{A, B}`, `as:`, `__MODULE__` and the
implicit nested-module alias — so a call written `Repo.insert(...)`
carries `MyApp.Repo::insert` and resolves by exact qualified name
instead of by bare name.
- Clause merging by (module, name, arity). The GenServer idiom spells a
multi-clause function as repeated `def`s, which otherwise index as one
identical node per clause and scatter every caller edge.
- Macro-argument dispatch, closed end to end rather than half-bridged:
Phoenix routes become `route` nodes linked to the controller action
they dispatch to (nested `scope` path + alias composition, `resources`
expansion, `forward`, `live`); `plug :atom` / `plug Module` link to the
function or `call/2` that runs; Ecto schema fields and association
target modules are extracted.
Kernel special forms (`case`, `if`, `quote`, `raise`, …) are suppressed
as call refs — they are syntax, and on plug they were ~1,900 refs that
could only ever resolve wrongly.
Validated on plug (S), phoenix (M) and firezone (L, 2,111 files):
extraction PASS on all three, 137/144 route→action edges resolved at
100% precision on firezone, node/edge counts stable across re-index and
incremental sync. Agent A/B (sonnet/high, 2 runs/arm) takes Read to ~0
and cuts tool calls 4-8x on every repo; wall-clock only improves on the
large repo, which is recorded honestly in the coverage playbook.
Grammar is the tree-sitter-wasms tree-sitter-elixir build (ABI 14),
health-checked — no vendored wasm needed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Bsi9EH64kMisnik4E1gf7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Elixir's grammar has no declaration node types —
defmodule,def,alias, an Ectoschemaand a Phoenix route are all the samecallnode, told apart only by the text of the target identifier. So the generic node-type ladder has nothing to match and every construct is dispatched through the visitNode hook, which also owns call extraction (visitFunctionBody does not invoke the hook, and Elixir call sites live inside bodies).Three things beyond plain symbol extraction:
alias Foo.{A, B},as:,__MODULE__and the implicit nested-module alias — so a call writtenRepo.insert(...)carriesMyApp.Repo::insertand resolves by exact qualified name instead of by bare name.defs, which otherwise index as one identical node per clause and scatter every caller edge.routenodes linked to the controller action they dispatch to (nestedscopepath + alias composition,resourcesexpansion,forward,live);plug :atom/plug Modulelink to the function orcall/2that runs; Ecto schema fields and association target modules are extracted.Kernel special forms (
case,if,quote,raise, …) are suppressed as call refs — they are syntax, and on plug they were ~1,900 refs that could only ever resolve wrongly.Validated on plug (S), phoenix (M) and firezone (L, 2,111 files): extraction PASS on all three, 137/144 route→action edges resolved at 100% precision on firezone, node/edge counts stable across re-index and incremental sync. Agent A/B (sonnet/high, 2 runs/arm) takes Read to ~0 and cuts tool calls 4-8x on every repo; wall-clock only improves on the large repo, which is recorded honestly in the coverage playbook.
Grammar is the tree-sitter-wasms tree-sitter-elixir build (ABI 14), health-checked — no vendored wasm needed.
Claude-Session: https://claude.ai/code/session_013Bsi9EH64kMisnik4E1gf7