use rustc_data_structures::fx::FxHashSet; use rustc_hir::CRATE_OWNER_ID; use rustc_middle::lint::LintExpectation; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::LintExpectationId; use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS; use rustc_span::Symbol; use crate::lints::{Expectation, ExpectationNote}; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { lint_expectations, check_expectations, ..*providers }; } fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> { let krate = tcx.hir_crate_items(()); let mut expectations = Vec::new(); for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) { let lints = tcx.shallow_lint_levels_on(owner); expectations.extend_from_slice(&lints.expectations); } expectations } fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { let lint_expectations = tcx.lint_expectations(()); let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids(); // Turn a `LintExpectationId` into a `(AttrId, lint_index)` pair. let canonicalize_id = |expect_id: &LintExpectationId| { match *expect_id { LintExpectationId::Unstable { attr_id, lint_index: Some(lint_index) } => { (attr_id, lint_index) } LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id; (attr_id, lint_index) } _ => panic!("fulfilled expectations must have a lint index"), } }; let fulfilled_expectations: FxHashSet<_> = fulfilled_expectations.iter().map(canonicalize_id).collect(); for (expect_id, expectation) in lint_expectations { // This check will always be true, since `lint_expectations` only holds stable ids let LintExpectationId::Stable { hir_id, .. } = expect_id else { unreachable!("at this stage all `LintExpectationId`s are stable"); }; let expect_id = canonicalize_id(expect_id); if !fulfilled_expectations.contains(&expect_id) && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) { let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale }); let note = expectation.is_unfulfilled_lint_expectations; tcx.emit_node_span_lint( UNFULFILLED_LINT_EXPECTATIONS, *hir_id, expectation.emission_span, Expectation { rationale, note }, ); } } }