Skip to content

feat(rust_analyzer): treat crates from locally-overridden modules as workspace members - #4214

Draft
lsjostro wants to merge 1 commit into
bazelbuild:mainfrom
lsjostro:local-override-workspace-members
Draft

feat(rust_analyzer): treat crates from locally-overridden modules as workspace members#4214
lsjostro wants to merge 1 commit into
bazelbuild:mainfrom
lsjostro:local-override-workspace-members

Conversation

@lsjostro

@lsjostro lsjostro commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #4213 (the Bug 2 follow-up from #4057). Opening as a draft — this is a concrete proposal for the direction discussion in the issue; happy to rework or split it based on feedback.

Problem

Crates from modules declared with local_path_override (or --override_module) are first-party code the user edits daily, but they sit under {output_base}/external/<repo>/, so the aspect marks them is_workspace_member: false and rust-analyzer shows no diagnostics for them.

Approach

This implements the "combined" option from #4213: ask Bazel which repos are really local overrides, and only touch crates under those repos.

Detection (tools/rust_analyzer/overrides.rs, new):

  • bazel mod graph --output json — modules with a non-registry override render with the version placeholder _ in their key (e.g. mypkg@_; the version field still carries the declared version, so the key is the only reliable marker). The whole graph is walked, so overrides of transitively-used modules are found too.
  • bazel mod dump_repo_mapping '' — resolves module names to canonical repo names authoritatively, instead of hard-coding a canonical-name scheme that has changed across Bazel versions. (Fallback <name>+ for overridden modules not visible from the root module.)
  • Only repos whose directory is a symlink count as local — that's what path-based overrides produce, and it excludes git_override/archive_override fetches, which are not editable checkouts.
  • In WORKSPACE mode bazel mod fails; detection logs at debug level and degrades to today's behavior.

Assembly (rust_project.rs): crates whose root_module falls under a detected root are marked workspace members, and their root_module + source.include_dirs are canonicalized in lockstep — editors put the real path in file URIs and rust-analyzer's file→crate mapping compares paths textually; canonicalizing only one of the two breaks the mapping and every item from the crate resolves as an unresolved import (we learned this in production). Generated sources live under bazel-out/, never under an override root, so they are untouched.

Cache (cache.rs): the override set is hashed into the merge cache key and the schema version is bumped. Flipping an override does not necessarily change any crate spec content (the canonical repo name — and thus every path in the specs — can stay identical), so it must invalidate the cache on its own.

Cost

Two extra bazel mod invocations per project generation (no analysis, answered by the running server); the second one is skipped when no overrides exist.

Known limitation

The aspect only emits build info (labels for runnables/flycheck) for crates it considers local, so override-module crates gain diagnostics but not codelens runnables. That would need an aspect-side change and feels like a separate discussion.

Testing

  • Unit tests for the graph parsing (override marker, nested overrides, no-override, malformed input), membership marking, cache-key sensitivity, and a unix test with a real symlinked repo verifying path canonicalization.
  • bazel test //tools/rust_analyzer:gen_rust_project_lib_test (51 tests) and //tools/rust_analyzer:gen_rust_project_clippy pass; rustfmt clean.
  • The bazel mod output shapes were verified against a real Bazel 9 bzlmod workspace with five local_path_override modules; we run the equivalent of this fix as a patch in our monorepo, where it restores diagnostics for all override-module crates.

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

…workspace members

Modules declared with local_path_override (or --override_module) have
their sources in a checkout the user edits directly, surfaced through a
symlink at {output_base}/external/<repo>/. The rust-analyzer aspect
classifies every crate under external/ as a non-member, so rust-analyzer
shows no diagnostics for these first-party crates.

Ask Bazel which external repos are local overrides: `bazel mod graph
--output json` marks modules with non-registry overrides with the
version placeholder `_` in their key, and `bazel mod dump_repo_mapping`
resolves module names to canonical repo names. Repos whose directory is
a symlink (what path-based overrides produce, unlike git_override /
archive_override fetches) are treated as local.

Crates under those roots are marked as workspace members, and their
root_module and source.include_dirs are canonicalized in lockstep so
rust-analyzer's textual file-to-crate mapping works with the real paths
editors put in file URIs.

The override set is hashed into the merge cache key (and the schema
version bumped): flipping an override does not necessarily change any
crate spec content, so it must invalidate the cache on its own. In
WORKSPACE mode `bazel mod` is unavailable; detection degrades to the
previous behavior.

Fixes bazelbuild#4213.

Assisted-by: Claude (Anthropic AI assistant)
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.

gen_rust_project: crates from local_path_override modules are not marked as workspace members

1 participant