Skip to main content

Rust adapter

from callix import RustAdapter

graph = RustAdapter().analyze("path/to/workspace")
RustAdapter(resolve=False).analyze(root) # structure only
PropertyValue
Language idrust
Project markerCargo.toml
Extensions.rs
Resolverrust-analyzer scip index
External requirementsrust-analyzer and Cargo on PATH

Workspaces with several members are handled: every Cargo.toml under the path is a crate root, and one index serves all of them.

Module names

A MODULE is a Rust module path derived from the file's place under src/:

src/net/http.rs → crate_name::net::http
src/lib.rs → crate_name

Trailing lib, main and mod are dropped — they name the enclosing module, not themselves. Inline mod foo { … } blocks add their own segment while the visitor is inside them.

What it extracts

Functions, structs, enums, traits and unions, type aliases, constants and statics, impl blocks with their methods, and use declarations.

impl blocks are dispatched in a second pass. Rust does not require a type to be declared above its impl, so an impl Trait for Type written before struct Type would otherwise find no type node and lose the base occurrence that becomes INHERITS_FROM.

Dependency classification

crate, self, super and the crate's own name are internal; std, core, alloc, proc_macro and test are the standard library; anything listed in dependencies, dev-dependencies or build-dependencies is third-party. Hyphens are normalized to underscores, since Cargo.toml says tree-sitter where the code says tree_sitter.

Boundaries

axum .route() routes — where the verb comes from the handler wrapper, as in .route("/x", get(handler)) — actix and rocket #[get("/x")] attributes, reqwest clients, tonic gRPC stubs, and queues.

The resolver

Resolution runs on the batch index produced by rust-analyzer scip, not on an interactive LSP server. The server keeps the whole workspace's analysis state resident and grows to tens of gigabytes on large projects; the index is written once and read statically.

callix decodes SCIP itself, without a protobuf runtime — only three fields per occurrence are needed: the symbol, the roles bitfield, and the start of the range.

:::note Where the time goes The index build dominates. On astral-sh/ruff — 1,900 files — the subprocess takes around 96 seconds of the roughly 100 the analysis needs; the lookups afterwards cost under a second. Rust throughput in the benchmarks is therefore mostly a measurement of rust-analyzer, not of callix. :::

Toolchain pinning

rust-analyzer on PATH is usually a rustup proxy honouring the project's rust-toolchain.toml. When that pinned toolchain lacks the component — ruff pins one that does — the proxy exits with Unknown binary and resolution would silently return nothing. callix asks rustup which from the project root first, then falls back to the default toolchain.