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]>
Stan Yolo 3 weeks ago
parent 0069617 · commit b633e68
-rw-r--r--crates/hir/src/lib.rs23
-rw-r--r--crates/ide/src/hover/render.rs6
-rw-r--r--crates/rust-analyzer/src/cli.rs5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs10
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()),
)