Skip to content

feat: add Erlang language support (.erl, .hrl)#830

Open
malou996 wants to merge 7 commits into
colbymchenry:mainfrom
malou996:main
Open

feat: add Erlang language support (.erl, .hrl)#830
malou996 wants to merge 7 commits into
colbymchenry:mainfrom
malou996:main

Conversation

@malou996

Copy link
Copy Markdown

Summary

  • Adds Erlang as a supported language with full extraction and cross-module resolution, benchmarked on poolboy, cowboy, and EMQX.
  • Vendored the WhatsApp tree-sitter-erlang WASM (ABI 14) — the tree-sitter-wasms build doesn't ship one.

Extraction (5 commits, layered)

Commit What
aec1357 Core wiring: grammar WASM, extractor, extension detection, corpus entries, README, CHANGELOG
6ae8303 Remote call resolution — module:function() calls resolve across .erl files without imports
3a84653 -behaviour(Name). declarations emit implements edges; OTP stdlib behaviours gracefully resolve to null
3d186f1 .app.src / .app OTP application metadata — application name as module node, applications list as import nodes
3268159 .hrl include resolution — -include/-include_lib connect to header files via file→file imports edges

What's extracted

  • Modules (-module(Name).), records (-record(Name, {...}).) with fields
  • Functions (multi-clause fun_decl), with name/arity signatures (handle_msg/2)
  • Type declarations (-type, -opaque)
  • Macros (-define(NAME, ...).constant nodes)
  • Exports (-export([...]).)
  • Remote calls (gen_server:start_link(...)) → cross-module calls edges
  • Behaviours (-behaviour(gen_server).) → implements edges + callback synthesis
  • Includes (-include, -include_lib) → imports edges to .hrl files
  • OTP app metadata (.app.src) → module + dependency import nodes

Test plan

  • 11 Erlang extraction tests pass (npx vitest run __tests__/extraction.test.ts -t "Erlang")
  • Full suite green (105 resolution tests, 374 extraction tests)
  • Verified on poolboy: poolboy.app.src → module poolboy + imports kernel/stdlib
  • Verified end-to-end .hrl resolution: my_server.erlinclude/records.hrl imports edge
  • Verified behaviour resolution: no false implements edges (module-only matching, name-matcher bypass)

leihua added 5 commits June 12, 2026 20:46
Wire the WhatsApp tree-sitter-erlang grammar into the extraction pipeline
with a custom visitNode extractor that handles Erlang's fun_decl/function_clause
AST structure. Extracts modules, functions, records, type/opaque declarations,
macros, imports/includes, exports, and call edges. Benchmarked on poolboy,
cowboy, and EMQX — codegraph reduces file reads to zero across all three.
Erlang remote calls like `gen_server:call(...)` now emit qualified
`module:function` references that resolve to functions in the target
module file. On cowboy, this creates 793 cross-file call edges where
previously all calls were file-local.

- extractCall: detect `call` nodes inside `remote` parents and emit
  `module:function` as the callee name
- import-resolver: add resolveErlangRemoteCall() that matches the
  module prefix to the target .erl file stem
- test: verify gen_server:start_link, gen_server:call, my_db:lookup
  are extracted as qualified references
Erlang -behaviour(gen_server). now creates module→behaviour implements
edges instead of import nodes. For in-project behaviours, the edge
resolves to the actual module; for OTP stdlib behaviours, the reference
stays unresolved rather than mis-connecting to a same-named field.
On cowboy, this produces 27 correct implements edges (e.g.
cowboy_router → cowboy_middleware). A callback synthesizer bridges
behaviour function dispatch for in-project behaviours.

- erlang.ts: behaviour_attribute emits implements unresolved reference
- import-resolver: resolveErlangBehaviourImplements matches module nodes
- index.ts: skip name-matcher for Erlang implements (wrong edge > missing)
- callback-synthesizer: erlangBehaviourEdges bridges dispatch + adds
  erlang to IFACE_OVERRIDE_LANGS
Parse .app.src / .app files as Erlang, extracting the application name
as a module node and the applications list as import nodes for
inter-app dependency tracking.
Connect -include/-include_lib directives to their target .hrl files
via file→file imports edges. Resolves same-directory includes first,
then falls back to project-wide search.
@malou996 malou996 closed this Jun 13, 2026
leihua added 2 commits June 13, 2026 11:35
…queries

The pre-filter, symbol lookup, and CLI commands all handled '.', '::',
and '/' separators but missed Erlang's single-colon 'module:function'
syntax. This caused 'mdb:dirty_insert' references to be dropped before
the Erlang remote call resolver could match them, and made callers/
callees/impact return empty results when queried with the qualified name.
@malou996 malou996 reopened this Jun 13, 2026
@malou996

Copy link
Copy Markdown
Author

Bug fix: module:function syntax in queries
The resolver's hasAnyPossibleMatch pre-filter handled ., ::, and / separators but missed Erlang's single-colon :. This caused all mdb:dirty_insert-style references to be silently dropped before resolveErlangRemoteCall could run. The same issue affected the CLI callers/callees/impact commands and the MCP findAllSymbols lookup — all three now strip the module prefix so agents can query with the natural module:function format.

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.

1 participant