Unnamed repository; edit this file 'description' to name the repository.
internal: add `Module::path_segments`, dedup the path-to-root name walk
Five call sites spelled out the same walk by hand — `path_to_root`, reverse,
`filter_map` the names — before rendering it as `::`-joined text:
`ModuleDef::canonical_path`, `cli::full_name_of_item`, two in `analysis_stats`,
and hover's path rendering.
`Module::path_segments` names that walk once and returns `Name`s rather than a
finished `String`: the call sites disagree on the edition to display with (the
CLI ones pin `Edition::LATEST`, hover takes it as a parameter), so joining
inside the helper would have fit none of them.
`ModuleDef::canonical_module_path` is the nearby sibling that yields the `Module`s
themselves; it does not serve these sites. Four of the five hold only a `Module`,
and it hangs off `ModuleDef` — while routing the fifth, `canonical_path`, through
it would put the `filter_map` this change removes straight back. The two are
cross-referenced in the docs instead.
Left alone: the two walks in `runnables`, which display each segment with its
own module's edition and so need the `Module`, not just its name.
No behavior change, with one wrinkle: `canonical_path` now renders each segment
into a `String` before joining. The iterator is lazy and owns its `Name`s, so a
borrowed `impl Display` cannot escape the closure — the old code got away with it
by collecting into a `Vec` first. The other four sites already did this.
This change was written with the assistance of an AI coding agent (Claude); a
human directed and reviewed the work.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
| -rw-r--r-- | crates/hir/src/lib.rs | 23 | ||||
| -rw-r--r-- | crates/ide/src/hover/render.rs | 6 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/cli.rs | 5 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/cli/analysis_stats.rs | 10 |
4 files changed, 21 insertions, 23 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ab7d88eeef..70e2d3faf1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -438,12 +438,9 @@ impl ModuleDef { } pub fn canonical_path(&self, db: &dyn HirDatabase, edition: Edition) -> Option<String> { - let mut segments = vec![self.name(db)?]; - for m in self.module(db)?.path_to_root(db) { - segments.extend(m.name(db)) - } - segments.reverse(); - Some(segments.iter().map(|it| it.display(db, edition)).join("::")) + let name = self.name(db)?; + let segments = self.module(db)?.path_segments(db).chain(Some(name)); + Some(segments.map(|it| it.display(db, edition).to_string()).join("::")) } pub fn canonical_module_path( @@ -679,6 +676,20 @@ impl Module { res } + /// Names of the modules enclosing `self`, crate root first, `self` last. + /// + /// Nameless modules — the crate root, and block modules — drop out, so this is + /// generally shorter than [`Module::path_to_root`]. Segments stay `Name`s rather + /// than rendered text because callers disagree on the edition to display with, + /// and some need to take the path apart rather than print it. + /// + /// [`ModuleDef::canonical_module_path`] is the same walk yielding the `Module`s + /// themselves, for callers that need more than the name — each module's own + /// edition, say. + pub fn path_segments(self, db: &dyn HirDatabase) -> impl Iterator<Item = Name> { + self.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)) + } + pub fn modules_in_scope(&self, db: &dyn HirDatabase, pub_only: bool) -> Vec<(Name, Module)> { let def_map = self.id.def_map(db); let scope = &def_map[self.id].scope; diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 88d21b23e8..f26b992929 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -439,11 +439,7 @@ pub(super) fn path( edition: Edition, ) -> String { let crate_name = module.krate(db).display_name(db).as_ref().map(|it| it.to_string()); - let module_path = module - .path_to_root(db) - .into_iter() - .rev() - .flat_map(|it| it.name(db).map(|name| name.display(db, edition).to_string())); + let module_path = module.path_segments(db).map(|it| it.display(db, edition).to_string()); crate_name.into_iter().chain(module_path).chain(item_name).join("::") } diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index 6643037220..cc0efe9ee9 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs @@ -82,10 +82,7 @@ fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) { fn full_name_of_item(db: &dyn HirDatabase, module: Module, name: Name) -> String { module - .path_to_root(db) - .into_iter() - .rev() - .filter_map(|it| it.name(db)) + .path_segments(db) .chain(Some(name)) .map(|it| it.display(db, Edition::LATEST).to_string()) .join("::") diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index ae9a0cf094..87790ce2bc 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -753,10 +753,7 @@ impl flags::AnalysisStats { }; if verbosity.is_spammy() { let full_name = module - .path_to_root(db) - .into_iter() - .rev() - .filter_map(|it| it.name(db)) + .path_segments(db) .chain(Some(body.name(db).unwrap_or_else(Name::missing))) .map(|it| it.display(db, Edition::LATEST).to_string()) .join("::"); @@ -1487,10 +1484,7 @@ fn full_name(db: &RootDatabase, name: impl Fn() -> Option<Name>, module: hir::Mo .into_iter() .chain( module - .path_to_root(db) - .into_iter() - .filter_map(|it| it.name(db)) - .rev() + .path_segments(db) .chain(Some(name().unwrap_or_else(Name::missing))) .map(|it| it.display(db, Edition::LATEST).to_string()), ) |