From 80844b8595e70aa46541c0e9e4ba99dd14994936 Mon Sep 17 00:00:00 2001 From: xmakro Date: Thu, 11 Jun 2026 10:16:40 -0700 Subject: [PATCH] perf: drop the full-crate AST walk in check_unused --- compiler/rustc_resolve/src/check_unused.rs | 13 ++++++----- compiler/rustc_resolve/src/late.rs | 25 +++++++++++++++------- compiler/rustc_resolve/src/lib.rs | 5 +++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 32e4bb9262fa8..4c5c591ad94e3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -10,9 +10,9 @@ // // Checking for unused imports is split into three steps: // -// - `UnusedImportCheckVisitor` walks the AST to find all the unused imports -// inside of `UseTree`s, recording their `NodeId`s and grouping them by -// the parent `use` item +// - `UnusedImportCheckVisitor` visits the `use` items collected during late +// resolution to find all the unused imports inside of `UseTree`s, recording +// their `NodeId`s and grouping them by the parent `use` item // // - `calc_unused_spans` then walks over all the `use` items marked in the // previous step to collect the spans associated with the `NodeId`s and to @@ -410,7 +410,7 @@ fn calc_unused_spans( } impl Resolver<'_, '_> { - pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { + pub(crate) fn check_unused(&mut self, use_items: Vec<&ast::Item>) { let tcx = self.tcx; let mut maybe_unused_extern_crates = FxHashMap::default(); @@ -465,7 +465,10 @@ impl Resolver<'_, '_> { base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, }; - visit::walk_crate(&mut visitor, krate); + // `use_items` is in crate DFS order, so diagnostics and side effects are unchanged. + for item in use_items { + visitor.visit_item(item); + } visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5ae2aaadffae7..bf9d6370092ff 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5587,12 +5587,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of -/// lifetime generic parameters and function parameters. -struct ItemInfoCollector<'a, 'ra, 'tcx> { +/// lifetime generic parameters and function parameters. Also collects all `use` and +/// `extern crate` items so that `check_unused` doesn't need to walk the crate again. +struct ItemInfoCollector<'a, 'ast, 'ra, 'tcx> { r: &'a mut Resolver<'ra, 'tcx>, + /// All `use` and `extern crate` items, in the order in which they are visited. + use_items: Vec<&'ast Item>, } -impl ItemInfoCollector<'_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_, '_> { fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { self.r .delegation_fn_sigs @@ -5626,7 +5629,7 @@ fn required_generic_args_suggestion(generics: &ast::Generics) -> Option if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } } -impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { +impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) @@ -5659,11 +5662,13 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } } + ItemKind::Use(..) | ItemKind::ExternCrate(..) => { + self.use_items.push(item); + } + ItemKind::Mod(..) | ItemKind::Static(..) | ItemKind::ConstBlock(..) - | ItemKind::Use(..) - | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) @@ -5694,9 +5699,12 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { + /// Returns the `use` and `extern crate` items of the crate, for use by `check_unused`. + pub(crate) fn late_resolve_crate<'ast>(&mut self, krate: &'ast Crate) -> Vec<&'ast Item> { with_owner(self, CRATE_NODE_ID, |this| { - visit::walk_crate(&mut ItemInfoCollector { r: this }, krate); + let mut info_collector = ItemInfoCollector { r: this, use_items: Vec::new() }; + visit::walk_crate(&mut info_collector, krate); + let use_items = info_collector.use_items; let mut late_resolution_visitor = LateResolutionVisitor::new(this); late_resolution_visitor .resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); @@ -5709,6 +5717,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { crate::diagnostics::UnusedLabel, ); } + use_items }) } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 061471ccc97e0..b8c9d42f6a6ab 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2082,9 +2082,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); - self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); + let use_items = + self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); - self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); + self.tcx.sess.time("resolve_check_unused", || self.check_unused(use_items)); self.tcx.sess.time("resolve_report_errors", || self.report_errors(krate)); self.tcx .sess