Rust Analyzer
rules_rust ships a one-shot installer that configures
rust-analyzer to use the project's Bazel
toolchain. After setup, rust-analyzer, the proc-macro server, and rustfmt
all come from Bazel — no host Rust install required.
Quick start
Pick your editor below. Each runs the same setup tool with a different
subcommand — setup is re-runnable any time.
VSCode
- Install the rust-analyzer extension.
-
bazel run @rules_rust//tools/rust_analyzer:setup -- vscode - Reload the VSCode window.
.vscode/settings.json is always written; a *.code-workspace at
the workspace root is picked up too. Existing user keys and comments
survive re-runs, so .vscode/settings.json and .code-workspace are
safe to commit.
Add the launcher dir to .gitignore:
.vscode/.rules_rust_analyzer/
Re-run setup after a toolchain change (rustup update, MODULE.bazel
edit, bazel clean --expunge).
Neovim
bazel run @rules_rust//tools/rust_analyzer:setup -- neovim
Prints an nvim-lspconfig Lua snippet to stdout. Paste it into your
init.lua (or pipe to a file you require). Restart Neovim.
For rustaceanvim users:
pass the printed cmd and settings table through its server
option (vim.g.rustaceanvim = { server = { cmd = ..., settings = ... } }).
Helix
bazel run @rules_rust//tools/rust_analyzer:setup -- helix
Prints a languages.toml snippet. Paste it into
<workspace>/.helix/languages.toml. Restart Helix.
Other editors (coc.nvim, vim-lsp, ALE, etc.)
bazel run @rules_rust//tools/rust_analyzer:setup -- print
Prints a generic JSON snippet using the rust-analyzer.* keys VSCode
uses. coc.nvim reads them via coc-settings.json (open with
:CocConfig); vim-lsp / ALE / LanguageClient-neovim accept the
same keys via plugin-specific config files.
Flags
Re-runnable at any time. Global flags work on any subcommand.
| Flag | Effect |
|---|---|
--skip-proc-macro-server | Don't manage the proc-macro key. |
--skip-rustfmt | Don't manage the formatter key (use host rustfmt). |
--per-package-workspaces / --no-per-package-workspaces | Opt this developer in/out of per-package workspace splitting (see below). |
--clippy / --no-clippy | Opt this developer in/out of running clippy on save and streaming its diagnostics alongside rustc's. |
--clean | Delete <launcher-dir>/cache/ before running the rest of setup. See Troubleshooting. |
The --clippy and --per-package-workspaces toggles are per-user: they mutate <launcher-dir>/user_config.json (gitignored) instead of the shared committed settings file. Two developers on the same workspace can hold different preferences without touching the checked-in configuration. Editing user_config.json by hand works too.
The vscode subcommand adds:
| Flag | Effect |
|---|---|
--settings-json <path> | Override the settings.json output. Defaults to <workspace>/.vscode/settings.json. |
--code-workspace <path> | .code-workspace file to also update. Required when the workspace root has more than one. |
--no-code-workspace | Skip the .code-workspace write. |
--settings-key <key> | Nest managed keys under this key inside the .code-workspace (default: settings). |
--dry-run | Print each would-be-written file to stdout. |
--replace | Overwrite managed keys instead of merging. Sibling folders / tasks / extensions in a .code-workspace survive. |
What you get
▶ Run Tests/▶ Run Testcodelens on every#[cfg(test)] modand individual#[test].- On-save squiggles from rustc diagnostics. Matches
cargo check— errors anywhere in the dep graph surface at their actual file paths. - Format-on-save via the Bazel-toolchain rustfmt.
- Workspace reload on watched
BUILD/MODULE.bazelchanges.
Troubleshooting
Symbols / deps look wrong
Restart rust-analyzer (or save a BUILD file). If that doesn't fix
it, re-run setup with --clean to nuke the discovery cache:
bazel run @rules_rust//tools/rust_analyzer:setup -- --clean vscode
Works with any subcommand (vscode / neovim / helix / print).
Diagnostics stopped appearing
Check <workspace>/.rules_rust_analyzer/flycheck.log.
After bazel clean --expunge or toolchain changes
Re-run setup.
Noisy cargo metadata errors on startup
setup does not manage rust-analyzer.files.excludeDirs. If your
workspace has stub Cargo.toml files that aren't meant to be
auto-loaded (common in rules_rust itself under examples/,
crate_universe/, etc.), rust-analyzer still finds them and logs
errors. Silence them by adding the directory names to settings.json
yourself — your entries survive future setup runs:
"rust-analyzer.files.excludeDirs": ["examples", "some_other_dir"]
Trade-off: files.excludeDirs also hides those sources from
rust-analyzer's virtual filesystem, so files under those directories
won't get IDE features even if they're part of a Bazel-discovered
crate. Only exclude directories whose sources you're willing to lose
IDE support on.
Workspace splitting
By default the whole project is treated as a single workspace.
For monorepos where indexing the whole graph is too slow, pass
--per-package-workspaces. Discover then scopes to the saved file's
package + deps; rust-analyzer reloads when you jump to a different
package. Caveat: dependents of the package you're working on aren't
indexed, so "find usages" can miss callers in other packages.
Switch any time by re-running setup with or without the flag.
Debugging
The ▶ Debug codelens VSCode renders next to #[test] functions
does not work for Bazel projects. Use .vscode/launch.json + F5
instead:
bazel run @rules_rust//tools/vscode:gen_launch_json
Install CodeLLDB first. Set a breakpoint inside the test and run the target — one launch config covers every test in that binary.