diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 227bff83cabbf..1c898814b9aa3 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -337,17 +337,6 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( mod_id: LocalModId, builtin_lints: T, ) { - let context = LateContext { - tcx, - enclosing_body: None, - cached_typeck_results: Cell::new(None), - param_env: ty::ParamEnv::empty(), - effective_visibilities: tcx.effective_visibilities(()), - last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(mod_id), - generics: None, - only_module: true, - }; - let skippable_lints = tcx.skippable_lints(()); // Note: `passes` is often empty. In that case, it's faster to run @@ -362,23 +351,33 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( let builtin_lints_must_run = is_lint_pass_required(skippable_lints, &builtin_lints.get_lints()); if passes.is_empty() { if builtin_lints_must_run { - late_lint_mod_inner(tcx, mod_id, context, builtin_lints); + late_lint_mod_inner(tcx, mod_id, builtin_lints); } } else { if builtin_lints_must_run { passes.push(Box::new(builtin_lints) as Box>); } let pass = RuntimeCombinedLateLintPass { passes }; - late_lint_mod_inner(tcx, mod_id, context, pass); + late_lint_mod_inner(tcx, mod_id, pass); } } fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( tcx: TyCtxt<'tcx>, mod_id: LocalModId, - context: LateContext<'tcx>, pass: T, ) { + let context = LateContext { + tcx, + enclosing_body: None, + cached_typeck_results: Cell::new(None), + param_env: ty::ParamEnv::empty(), + effective_visibilities: tcx.effective_visibilities(()), + last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(mod_id), + generics: None, + only_module: true, + }; + let mut cx = LateContextAndPass { context, pass }; let (module, _span, hir_id) = tcx.hir_get_module(mod_id); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 5671e86283f9b..b0e5e3b2362a9 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -117,6 +117,7 @@ impl LintLevelSets { fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet { let store = unerased_lint_store(&tcx.sess); let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID); + let lint_cap_allow = tcx.sess.opts.lint_cap == Some(Level::Allow); let mut skippable: FxHashSet = store .get_lints() @@ -125,13 +126,18 @@ fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet { // Lints that show up in future-compat reports must always be run. let has_future_breakage = lint.future_incompatible.is_some_and(|fut| fut.report_in_deps); - !has_future_breakage && !lint.eval_always + // `-Zfuture-incompat-test` forces non-allow lints to report as future-compat + let test = tcx.sess.opts.unstable_opts.future_incompat_test + && lint.default_level != Level::Allow; + !has_future_breakage && !test && !lint.eval_always }) .filter(|lint| { let level_spec = root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); - // Only include lints that are allowed at crate root or by default. - level_spec.is_allow() + let level = level_spec.level(); + // Only include lints that are allowed at crate root, or by capping, or by default. + level == Level::Allow + || (lint_cap_allow && matches!(level, Level::Warn | Level::Deny | Level::Forbid)) || (matches!(level_spec.src, LintLevelSource::Default) && lint.default_level(tcx.sess.edition()) == Level::Allow) }) @@ -144,8 +150,12 @@ fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet { // All lints that appear with a non-allow level must be run. for (_, specs) in map.specs.iter() { for (lint, level_spec) in specs.iter() { - if !level_spec.is_allow() { - skippable.remove(lint); + match level_spec.level() { + Level::Allow => {} + Level::Warn | Level::Deny | Level::Forbid if lint_cap_allow => {} + _ => { + skippable.remove(lint); + } } } } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 28a35013b6516..5a431440bd528 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1198,6 +1198,7 @@ rustc_queries! { desc { "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } } + /// Checks for liveness of variables within a function. No-op if unused lints are skippable. query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet { arena_cache desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 32951ea0162a6..5ae42a738cb75 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -14,7 +14,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor}; -use rustc_session::lint; +use rustc_session::lint::LintId; +use rustc_session::lint::builtin::{UNUSED_ASSIGNMENTS, UNUSED_VARIABLES}; use rustc_span::Span; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{Symbol, kw, sym}; @@ -53,6 +54,13 @@ struct Access { #[tracing::instrument(level = "debug", skip(tcx), ret)] pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> DenseBitSet { + let skippable_lints = tcx.skippable_lints(()); + if skippable_lints.contains(&LintId::of(UNUSED_ASSIGNMENTS)) + && skippable_lints.contains(&LintId::of(UNUSED_VARIABLES)) + { + return DenseBitSet::new_empty(0); + } + // Don't run on synthetic MIR, as that will ICE trying to access HIR. if tcx.is_synthetic_mir(def_id) { return DenseBitSet::new_empty(0); @@ -1074,7 +1082,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { diagnostics::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name, typo } }; tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, + UNUSED_VARIABLES, hir_id, def_span, diagnostics::UnusedVariable { @@ -1124,7 +1132,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let typo = maybe_suggest_typo(); tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, + UNUSED_VARIABLES, hir_id, def_span, diagnostics::UnusedVarAssignedOnly { name, typo }, @@ -1166,7 +1174,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { }; tcx.emit_node_span_lint( - lint::builtin::UNUSED_VARIABLES, + UNUSED_VARIABLES, hir_id, spans, diagnostics::UnusedVariable { @@ -1258,20 +1266,20 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { if suggestion.is_none() && is_direct { overwrite } else { None }; let help = suggestion.is_none() && overwrite.is_none(); tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, + UNUSED_ASSIGNMENTS, hir_id, source_info.span, diagnostics::UnusedAssign { name, overwrite, help, suggestion }, ) } AccessKind::Param => tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, + UNUSED_ASSIGNMENTS, hir_id, source_info.span, diagnostics::UnusedAssignPassed { name }, ), AccessKind::Capture => tcx.emit_node_span_lint( - lint::builtin::UNUSED_ASSIGNMENTS, + UNUSED_ASSIGNMENTS, hir_id, decl_span, diagnostics::UnusedCaptureMaybeCaptureRef { name },