From a7331929804d44db4ec13206477f3a2df558b891 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 25 Mar 2026 06:58:13 +1100 Subject: [PATCH] Make `rustc_hir_analysis` not depend on `rustc_lint`. `rustc_hir_analysis` depends on `rustc_lint` in just a single function: `emit_delayed_lint`, which is used by the "emit_ast_lowering_delayed_lints" checking section within `rustc_hir_analysis::check_crate`. This commit moves that function and section to out of `rustc_hir_analysis::check_crate`, into `rustc_interface`, eliminating the dependency. This seems reasonable because the delayed lint errors aren't really related to HIR analysis. They were in there just because HIR analysis follows AST lowering. This means `rustc_hir_analysis` and `rustc_lint` can both start compiling as soon as `rustc_trait_selection` finishes. This also changes the error order in one test, which doesn't matter. The commit also changes `emit_delayed_lint` to `emit_delayed_lints`, factoring out some code duplicated in rustdoc. --- Cargo.lock | 1 - compiler/rustc_hir_analysis/Cargo.toml | 1 - compiler/rustc_hir_analysis/src/lib.rs | 55 ------------------- compiler/rustc_interface/src/passes.rs | 54 +++++++++++++++++- src/librustdoc/lib.rs | 8 +-- .../inline-trait-and-foreign-items.stderr | 16 +++--- 6 files changed, 62 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85997e1886f1e..91daa229a95e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4021,7 +4021,6 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", - "rustc_lint", "rustc_lint_defs", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 0ce93561ddd4d..0dbd4333ed3ca 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -19,7 +19,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } -rustc_lint = { path = "../rustc_lint" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0e8bc9ad58227..937bcee01161e 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -85,8 +85,6 @@ pub use errors::NoVariantNamed; use rustc_abi::{CVariadicStatus, ExternAbi}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::lints::DelayedLint; -use rustc_lint::DecorateAttrLint; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{Const, Ty, TyCtxt}; @@ -147,23 +145,6 @@ pub fn provide(providers: &mut Providers) { }; } -pub fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) { - match lint { - DelayedLint::AttributeParsing(attribute_lint) => { - tcx.emit_node_span_lint( - attribute_lint.lint_id.lint, - attribute_lint.id, - attribute_lint.span, - DecorateAttrLint { - sess: tcx.sess, - tcx: Some(tcx), - diagnostic: &attribute_lint.kind, - }, - ); - } - } -} - pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); @@ -182,42 +163,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _: R = tcx.ensure_result().crate_inherent_impls_overlap_check(()); }); - tcx.sess.time("emit_ast_lowering_delayed_lints", || { - // sanity check in debug mode that all lints are really noticed - // and we really will emit them all in the loop right below. - // - // during ast lowering, when creating items, foreign items, trait items and impl items - // we store in them whether they have any lints in their owner node that should be - // picked up by `hir_crate_items`. However, theoretically code can run between that - // boolean being inserted into the item and the owner node being created. - // We don't want any new lints to be emitted there - // (though honestly, you have to really try to manage to do that but still), - // but this check is there to catch that. - #[cfg(debug_assertions)] - { - // iterate over all owners - for owner_id in tcx.hir_crate_items(()).owners() { - // if it has delayed lints - if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { - if !delayed_lints.lints.is_empty() { - // assert that delayed_lint_items also picked up this item to have lints - assert!( - tcx.hir_crate_items(()).delayed_lint_items().any(|i| i == owner_id) - ); - } - } - } - } - - for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { - if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { - for lint in &delayed_lints.lints { - emit_delayed_lint(lint, tcx); - } - } - } - }); - tcx.par_hir_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); // Make sure we evaluate all static and (non-associated) const items, even if unused. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6b7f31521d441..d7d4f00578d1d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -21,9 +21,12 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; +use rustc_hir::lints::DelayedLint; use rustc_hir::{Attribute, MaybeOwner, find_attr}; use rustc_incremental::setup_dep_graph; -use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; +use rustc_lint::{ + BufferedEarlyLint, DecorateAttrLint, EarlyCheckNode, LintStore, unerased_lint_store, +}; use rustc_metadata::EncodedMetadata; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; @@ -1025,6 +1028,29 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( ) } +pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { + for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { + if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { + for lint in &delayed_lints.lints { + match lint { + DelayedLint::AttributeParsing(attribute_lint) => { + tcx.emit_node_span_lint( + attribute_lint.lint_id.lint, + attribute_lint.id, + attribute_lint.span, + DecorateAttrLint { + sess: tcx.sess, + tcx: Some(tcx), + diagnostic: &attribute_lint.kind, + }, + ); + } + } + } + } + } +} + /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. /// This function never fails. fn run_required_analyses(tcx: TyCtxt<'_>) { @@ -1074,6 +1100,32 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { ]); }); + sess.time("emit_ast_lowering_delayed_lints", || { + // Sanity check in debug mode that all lints are really noticed and we really will emit + // them all in the loop right below. + // + // During ast lowering, when creating items, foreign items, trait items and impl items, + // we store in them whether they have any lints in their owner node that should be + // picked up by `hir_crate_items`. However, theoretically code can run between that + // boolean being inserted into the item and the owner node being created. We don't want + // any new lints to be emitted there (you have to really try to manage that but still), + // but this check is there to catch that. + #[cfg(debug_assertions)] + { + let hir_items = tcx.hir_crate_items(()); + for owner_id in hir_items.owners() { + if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { + if !delayed_lints.lints.is_empty() { + // Assert that delayed_lint_items also picked up this item to have lints. + assert!(hir_items.delayed_lint_items().any(|i| i == owner_id)); + } + } + } + } + + emit_delayed_lints(tcx); + }); + rustc_hir_analysis::check_crate(tcx); // Freeze definitions as we don't add new ones at this point. // We need to wait until now since we synthesize a by-move body diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 6718505bdefd9..751db71ceff00 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -904,13 +904,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { return; } - for owner_id in tcx.hir_crate_items(()).delayed_lint_items() { - if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) { - for lint in &delayed_lints.lints { - rustc_hir_analysis::emit_delayed_lint(lint, tcx); - } - } - } + rustc_interface::passes::emit_delayed_lints(tcx); if render_opts.dep_info().is_some() { rustc_interface::passes::write_dep_info(tcx); diff --git a/tests/ui/lint/inline-trait-and-foreign-items.stderr b/tests/ui/lint/inline-trait-and-foreign-items.stderr index 4bde4bc590aa8..b12fa4d9c47f7 100644 --- a/tests/ui/lint/inline-trait-and-foreign-items.stderr +++ b/tests/ui/lint/inline-trait-and-foreign-items.stderr @@ -38,14 +38,6 @@ LL | #[inline] | = help: `#[inline]` can only be applied to functions -error: unconstrained opaque type - --> $DIR/inline-trait-and-foreign-items.rs:26:14 - | -LL | type U = impl Trait; - | ^^^^^^^^^^ - | - = note: `U` must be used in combination with a concrete type within the same impl - warning: `#[inline]` attribute cannot be used on associated consts --> $DIR/inline-trait-and-foreign-items.rs:7:5 | @@ -69,5 +61,13 @@ LL | #[inline] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[inline]` can only be applied to functions +error: unconstrained opaque type + --> $DIR/inline-trait-and-foreign-items.rs:26:14 + | +LL | type U = impl Trait; + | ^^^^^^^^^^ + | + = note: `U` must be used in combination with a concrete type within the same impl + error: aborting due to 6 previous errors; 2 warnings emitted