cargo-atlas
Health Warn
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 7 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
A compiler-accurate map of a Rust workspace for AI coding assistants
cargo-atlas
A map of a Rust workspace that is as accurate as the compiler, built for AI
coding assistants such as Claude Code. Ask "who calls this?", "what implements
this trait?" or "which tests reach this function?" and get exact answers withfile:line, instead of an assistant grepping through files.
Status: prototype (0.1). The commands and the MCP server work on real
workspaces. Claude Code skills come next.
Why
Tools that match function names guess. Two structs can both have a parse(),
and a name alone doesn't say which one r.parse() calls. rust-analyzer works
out the type of r, so it knows.
In the test fixture (tests/fixtures/spike), cargo-atlas links all 7 calls to
the exact function; Graphify, which matches names, links none of them. On
ripgrep 15.1.0:
- 50 random call links, checked by reading the code: 50 correct.
- 150 random call sites taken from the source text: every call into a workspace function was found.
- cargo-atlas finds 5,948 function-to-function call links, Graphify 2,809, and 39% of Graphify's comparable links point at a different function than rust-analyzer resolved.
Details and how to reproduce: bench/README.md.
Install
rustup component add rust-analyzer rust-src
cargo install --path . # from a clone; a crates.io release comes later
rust-src matters: without the standard library's source, rust-analyzer can't
expand macros like println! or infer std types, and calls hidden there go
missing. cargo atlas build warns if it's absent.
Use
cargo atlas build # index the workspace into .atlas/graph.json
cargo atlas callers JsonReader::parse # who calls it
cargo atlas callees run # what it calls
cargo atlas impls Loader # types implementing a trait (or a type's traits)
cargo atlas path main CsvReader::parse
cargo atlas explain run # kind, location, signature, every link
cargo atlas search parse # items whose name or path contains "parse"
cargo atlas tests JsonReader::parse # tests that reach it, and the command that runs them
cargo atlas unsafe --missing # unsafe code without its SAFETY comment
cargo atlas report # summary in .atlas/report.md
An item can be named three ways: parse, a path such as JsonReader::parse
(optionally with its crate first, spike::json_reader::JsonReader::parse), or
the location of its name, src/json_reader.rs:10. Generic arguments can be
left out (GlobBuilder::build for GlobBuilder<'a>::build), and a method in a
trait impl can be written Square::area for <Square as Area>::area.
Output on the fixture:
$ cargo atlas callees run
run (src/main.rs:9)
-> Loader::load src/main.rs:10 EXACT
-> <CsvReader as Loader>::load src/main.rs:10 CANDIDATE (through the trait)
-> <JsonReader as Loader>::load src/main.rs:10 CANDIDATE (through the trait)
run takes a &dyn Loader, so the impl that runs is chosen at runtime. The
call to the trait method is exact; the two impls are candidates. When a
function calls the same thing several times, every line is listed, such assrc/lib.rs:12,30,41.
A name that matches several items lists them:
$ cargo atlas callers parse
error: `parse` matches 2 items. Pick one by its full path or its location (for example `src/csv_reader.rs:10`):
spike::csv_reader::CsvReader::parse (src/csv_reader.rs:10)
spike::json_reader::JsonReader::parse (src/json_reader.rs:10)
Queries answer from the last build. When a file changed since then, they say
so on stderr: note: 1 file changed (src/lib.rs) since the last build.
Use it from Claude Code
cargo atlas serve answers the same questions as MCP tools on stdin and
stdout. Register it once:
claude mcp add --transport stdio cargo-atlas -- cargo-atlas serve
Add --scope user to have it in every project. In a folder that isn't a Cargo
workspace, the tools say so and do nothing else.
The tools are callers, callees, impls, path, explain, search,tests, unsafe_code and refresh. Each returns the same text as the
command line. The server:
does nothing until the first call, then loads
.atlas/graph.json, or
builds it if there is none (4 s for the edge fixture, 40 s for tokio);answers in 2 to 4 ms on tokio after that, including the check below;
checks before every answer whether an indexed file or
Cargo.tomlchanged
since the build. If one did, the answer starts with a note, and a rebuild
runs in the background:Note: 1 file changed (src/json_reader.rs) since the graph was built, so answers about code there may be out of date. A rebuild is running (the last build took 4 s); call `refresh` to wait for it.rebuilds and waits when
refreshis called;cuts answers at 24,000 characters (about 6,000 tokens), below the size at
which Claude Code warns.
A name that matches several items is an ordinary answer listing them, not an
error, since the next step is to pick one.
Tests and unsafe code
cargo atlas tests ITEM walks the links backwards from ITEM to every function
marked #[test], #[tokio::test], #[rstest] and the like, and prints acargo test command that runs only those tests:
$ cargo atlas tests first_unchecked
Tests that reach first_unchecked (edge-core/src/raw.rs:8)
first_of_empty_is_zero edge-core/src/raw.rs:79 via first_or_zero
Run them:
cargo test -p edge-core -- raw::tests::first_of_empty_is_zero
For a type it starts from the type's methods; for a trait, from the trait's
methods and every impl of them.
cargo atlas unsafe lists every unsafe block, fn, impl and trait, and
whether each explains itself the way clippy expects: a // SAFETY: comment
above a block or impl, a # Safety section in the docs of an unsafe fn or
trait. A Safety: line or a # Safety heading in a comment counts too, and anunsafe fn in a trait impl is covered by the trait's docs. With an item, it
lists the unsafe code inside it, and for a function also the unsafe code its
calls reach:
$ cargo atlas unsafe first_or_zero
Unsafe code in first_or_zero (edge-core/src/raw.rs:18): 1 site, 0 without a SAFETY comment or # Safety section
edge-core/src/raw.rs:22 block in first_or_zero SAFETY comment
Reached through its calls: 2 sites, 0 without a SAFETY comment or # Safety section
edge-core/src/raw.rs:8 fn first_unchecked safety documented
edge-core/src/raw.rs:10 block in first_unchecked SAFETY comment
The check looks for the marker, not for prose: a comment that explains the
reasoning without SAFETY: counts as missing.
How it works
Three sources feed one graph:
rust-analyzer sciplists every definition and reference in the
workspace, each resolved to one exact symbol, with each item's full span. A
reference inside a function's span becomes a link from that function.cargo metadatagives the crates and their dependencies.- A pass with
synfinds impl headers,#[derive]lists, test attributes
andunsafecode, including inside macro calls that wrap plain Rust, such
as tokio'scfg_rt! { ... }. The index then says which exact items those
names refer to.
rust-analyzer gets two settings. It turns on cfg(miri) by default, which
removes every item marked #[cfg(not(miri))] (40 of tokio's 153 test files),
so cargo-atlas turns it off. Features follow Cargo: the default ones, or--features a,b and --all-features on build and serve.
Every link carries a confidence label:
| Label | Meaning |
|---|---|
| EXACT | Resolved by rust-analyzer or Cargo |
| CANDIDATE | One of several possible targets, as with dyn Trait calls |
| SYNTAX | Read from source text but not type-checked, as with derives |
Limits
- Blanket impls such as
impl<T: Area> Named for Thave no concrete type,
so no type is linked toNamed. - Shared symbols: rust-analyzer gives some different items one symbol, such
asmaininbuild.rsand insrc/main.rs. Each still gets its own node, but
a link to one of them can be a guess; those links are marked CANDIDATE. - Proc macros (
#[tokio::main],sqlx::query!) are expanded once they
compile. Code from one that fails to build is missing. - Code for other platforms and features is not in the graph:
#[cfg(windows)]
items on Linux, or a feature that isn't on. Unsafe code in such items is
still listed, marked as not in the index. - A test or
unsafeblock inside amacro_rules!body isn't seen: the body
is a template, not code. testsfollows calls in the code. A test that runs the built program as a
subprocess, as ripgrep's integration tests do, calls nothing in the graph,
and neither does a call made through std, such as"..".parse::<Glob>()
reachingGlob::from_str.rust-analyzer scipis not a stable interface. Tested with rust-analyzer 0.3.3057.
Decisions and their reasons are logged in DECISIONS.md.
Development
cargo test # needs rust-analyzer and rust-src, like the tool itself
The end-to-end tests build two fixture workspaces in tests/fixtures and
compare the answers with ones written by reading the code. tests/server.rs
starts cargo atlas serve and talks MCP to it, as Claude Code does, including
an edit followed by refresh. Without rust-analyzer the tests fail with
install instructions; set CARGO_ATLAS_SKIP_RA_TESTS=1 to skip them on
purpose. CI runs fmt, clippy and the tests on every push.
License
MIT OR Apache-2.0, at your option.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found