rustc_lint/
expect.rs

1use rustc_data_structures::fx::FxHashSet;
2use rustc_hir::CRATE_OWNER_ID;
3use rustc_middle::lint::LintExpectation;
4use rustc_middle::query::Providers;
5use rustc_middle::ty::TyCtxt;
6use rustc_session::lint::LintExpectationId;
7use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
8use rustc_span::Symbol;
9
10use crate::lints::{Expectation, ExpectationNote};
11
12pub(crate) fn provide(providers: &mut Providers) {
13    *providers = Providers { lint_expectations, check_expectations, ..*providers };
14}
15
16fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
17    let krate = tcx.hir_crate_items(());
18
19    let mut expectations = Vec::new();
20
21    for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) {
22        let lints = tcx.shallow_lint_levels_on(owner);
23        expectations.extend_from_slice(&lints.expectations);
24    }
25
26    expectations
27}
28
29fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
30    let lint_expectations = tcx.lint_expectations(());
31    let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids();
32
33    // Turn a `LintExpectationId` into a `(AttrId, lint_index)` pair.
34    let canonicalize_id = |expect_id: &LintExpectationId| {
35        match *expect_id {
36            LintExpectationId::Unstable { attr_id, lint_index: Some(lint_index) } => {
37                (attr_id, lint_index)
38            }
39            LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
40                // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok.
41                let attr_id = tcx.hir_attrs(hir_id)[attr_index as usize].id();
42
43                (attr_id, lint_index)
44            }
45            _ => panic!("fulfilled expectations must have a lint index"),
46        }
47    };
48
49    let fulfilled_expectations: FxHashSet<_> =
50        fulfilled_expectations.iter().map(canonicalize_id).collect();
51
52    for (expect_id, expectation) in lint_expectations {
53        // This check will always be true, since `lint_expectations` only holds stable ids
54        let LintExpectationId::Stable { hir_id, .. } = expect_id else {
55            unreachable!("at this stage all `LintExpectationId`s are stable");
56        };
57
58        let expect_id = canonicalize_id(expect_id);
59
60        if !fulfilled_expectations.contains(&expect_id)
61            && tool_filter.is_none_or(|filter| expectation.lint_tool == Some(filter))
62        {
63            let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale });
64            let note = expectation.is_unfulfilled_lint_expectations;
65            tcx.emit_node_span_lint(
66                UNFULFILLED_LINT_EXPECTATIONS,
67                *hir_id,
68                expectation.emission_span,
69                Expectation { rationale, note },
70            );
71        }
72    }
73}