Skip to content

fix(rust_analyzer): resolve external crate sources via output_base, not execroot - #4212

Open
lsjostro wants to merge 1 commit into
bazelbuild:mainfrom
lsjostro:fix-rust-analyzer-external-paths
Open

fix(rust_analyzer): resolve external crate sources via output_base, not execroot#4212
lsjostro wants to merge 1 commit into
bazelbuild:mainfrom
lsjostro:fix-rust-analyzer-external-paths

Conversation

@lsjostro

@lsjostro lsjostro commented Aug 8, 2026

Copy link
Copy Markdown

Fixes the first bug described in #4057 (as requested by @UebelAndre there, this PR covers Bug 1 only; Bug 2 will get its own issue).

Problem

gen_rust_project emits external crate paths through the execution root:

{output_base}/execroot/_main/external/rules_rust+crate+anyhow-1.0.0/src/lib.rs

The execroot's external/ symlinks are ephemeral — they are only guaranteed to exist while a build action is running — so once the build finishes these paths dangle. rust-analyzer then cannot read any external crate sources: no completions, no go-to-definition, no inline docs for third-party dependencies (anyhow, serde, clap, …).

The stable location of external repository sources is {output_base}/external/.

Fix

In deserialize_with_substitution, rewrite {execution_root}/external/ prefixes to {output_base}/external/ after the existing template substitutions. bazel-out/ paths (generated files) are unaffected and stay under the execution root where they belong.

Testing

Added a unit test covering the four path shapes (external via __EXEC_ROOT__, generated file under bazel-out/, __OUTPUT_BASE__, and __WORKSPACE__); it fails without the fix and passes with it. We have also been running this fix as a patch on top of rules_rust in our bzlmod monorepo, where it restores full rust-analyzer IDE support for external crates.

Per the AI tools policy: this change was prepared with LLM assistance (see Assisted-by commit trailer), reviewed and tested by me.

…ot execroot

gen_rust_project emits external crate paths through the execution root
(__EXEC_ROOT__/external/...). The execroot's external/ symlinks are
ephemeral -- only guaranteed to exist while a build action is running --
so once the build finishes those paths dangle and rust-analyzer cannot
read any external crate sources. The result is no completions, no
go-to-definition, and no docs for third-party dependencies.

The real, stable location of external repository sources is
{output_base}/external/. Rewrite {execution_root}/external/ prefixes to
{output_base}/external/ during template substitution, while leaving
bazel-out/ paths (generated files) under the execution root where they
belong.

Fixes the first bug described in bazelbuild#4057.

Assisted-by: Claude (Anthropic AI assistant)
@lsjostro

Copy link
Copy Markdown
Author

Fresh datapoint on why this matters, re-measured today in our bzlmod monorepo (181 crates in rust-project.json across several crate closures):

Regenerating rust-project.json without this fix, 177 of the 181 crates — every third-party crate — get a root_module (plus CARGO_MANIFEST_DIR/CARGO_MANIFEST_PATH env) pointing into <output_base>/execroot/_main/external/...; 556 execroot path references in total. Those symlinks are rearranged by the next build and disappear on bazel clean, which breaks rust-analyzer's file→crate mapping (external crate items show up as unresolved imports).

With the fix, all 556 references resolve via <output_base>/external/ instead — zero execroot paths — and on Bazel 9 they canonicalize further to stable repo-contents-cache paths, so the project survives rebuilds and bazel clean.

(Measured against a current rules_rust pin carrying the same deserialize_with_substitution code; we ship this fix as a local patch in production in the meantime.)

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