| use rustc_data_structures::fx::FxHashSet; | |
| 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 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<Symbol>) { | |
| 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.is_none_or(|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 }, | |
| ); | |
| } | |
| } | |
| } |