Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions compiler/rustc_lint/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<dyn LateLintPass<'tcx>>);
}
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);
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ impl LintLevelSets {
fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
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<LintId> = store
.get_lints()
Expand All @@ -125,13 +126,18 @@ fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
// 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)
})
Expand All @@ -144,8 +150,12 @@ fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
// 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);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<abi::FieldIdx> {
arena_cache
desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) }
Expand Down
22 changes: 15 additions & 7 deletions compiler/rustc_mir_transform/src/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<FieldIdx> {
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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 },
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use rustc_middle::ty::{self, AssocTag, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::config::CrateType;
use rustc_session::lint::builtin::{DEAD_CODE, DEAD_CODE_PUB_IN_BINARY};
use rustc_session::lint::{self, Lint, StableLintExpectationId};
use rustc_session::lint::{self, Lint, LintId, StableLintExpectationId};
use rustc_span::{Symbol, kw};

use crate::diagnostics::{
Expand Down Expand Up @@ -1328,6 +1328,14 @@ impl<'tcx> DeadVisitor<'tcx> {
}

fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModId) {
let skippable_lints = tcx.skippable_lints(());
let is_exec = tcx.crate_types().contains(&CrateType::Executable);
if (!is_exec || skippable_lints.contains(&LintId::of(DEAD_CODE_PUB_IN_BINARY)))
&& skippable_lints.contains(&LintId::of(DEAD_CODE))
{
return;
}

let Ok(DeadCodeLivenessSummary { pre_deferred_seeding, final_result }) =
tcx.live_symbols_and_ignored_derived_traits(()).as_ref()
else {
Expand All @@ -1336,7 +1344,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModId) {

let module_items = tcx.hir_module_items(module);

if tcx.crate_types().contains(&CrateType::Executable) {
if is_exec {
let is_unused_pub = |def_id: LocalDefId| {
tcx.effective_visibilities(()).is_public_at_level(def_id, Level::Reachable)
&& !pre_deferred_seeding.live_symbols.contains(&def_id)
Expand Down
Loading