Skip to content

Commit 809e06b

Browse files
committed
Auto merge of #127313 - cjgillot:single-expect, r=<try>
Rewrite lint_expectations in a single pass. This PR aims at reducing the perf regression from #120924 (comment) with drive-by simplifications. Basically, instead of using the lint level builder, which is slow, this PR splits `lint_expectations` logic in 2: - listing the `LintExpectations` is done in `shallow_lint_levels_on`, on a per-owner basis; - building the unstable->stable expectation id map is done by iterating on attributes. r? ghost for perf
2 parents 486bc27 + 66a9985 commit 809e06b

15 files changed

+171
-382
lines changed

compiler/rustc_errors/src/diagnostic.rs

+7-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_lint_defs::{Applicability, LintExpectationId};
1010
use rustc_macros::{Decodable, Encodable};
1111
use rustc_span::source_map::Spanned;
1212
use rustc_span::symbol::Symbol;
13-
use rustc_span::{Span, DUMMY_SP};
13+
use rustc_span::{AttrId, Span, DUMMY_SP};
1414
use std::borrow::Cow;
1515
use std::fmt::{self, Debug};
1616
use std::hash::{Hash, Hasher};
@@ -355,24 +355,19 @@ impl DiagInner {
355355

356356
pub(crate) fn update_unstable_expectation_id(
357357
&mut self,
358-
unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>,
358+
unstable_to_stable: &FxIndexMap<AttrId, LintExpectationId>,
359359
) {
360360
if let Level::Expect(expectation_id) | Level::ForceWarning(Some(expectation_id)) =
361361
&mut self.level
362+
&& let LintExpectationId::Unstable { attr_id, lint_index } = *expectation_id
362363
{
363-
if expectation_id.is_stable() {
364-
return;
365-
}
366-
367364
// The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index.
368365
// The lint index inside the attribute is manually transferred here.
369-
let lint_index = expectation_id.get_lint_index();
370-
expectation_id.set_lint_index(None);
371-
let mut stable_id = unstable_to_stable
372-
.get(expectation_id)
373-
.expect("each unstable `LintExpectationId` must have a matching stable id")
374-
.normalize();
366+
let Some(stable_id) = unstable_to_stable.get(&attr_id) else {
367+
panic!("{expectation_id:?} must have a matching stable id")
368+
};
375369

370+
let mut stable_id = *stable_id;
376371
stable_id.set_lint_index(lint_index);
377372
*expectation_id = stable_id;
378373
}

compiler/rustc_errors/src/lib.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use rustc_data_structures::AtomicRef;
6060
use rustc_lint_defs::LintExpectationId;
6161
use rustc_macros::{Decodable, Encodable};
6262
use rustc_span::source_map::SourceMap;
63-
use rustc_span::{Loc, Span, DUMMY_SP};
63+
use rustc_span::{AttrId, Loc, Span, DUMMY_SP};
6464
use std::backtrace::{Backtrace, BacktraceStatus};
6565
use std::borrow::Cow;
6666
use std::cell::Cell;
@@ -1085,7 +1085,7 @@ impl<'a> DiagCtxtHandle<'a> {
10851085

10861086
pub fn update_unstable_expectation_id(
10871087
&self,
1088-
unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>,
1088+
unstable_to_stable: FxIndexMap<AttrId, LintExpectationId>,
10891089
) {
10901090
let mut inner = self.inner.borrow_mut();
10911091
let diags = std::mem::take(&mut inner.unstable_expect_diagnostics);
@@ -1094,7 +1094,7 @@ impl<'a> DiagCtxtHandle<'a> {
10941094
if !diags.is_empty() {
10951095
inner.suppressed_expected_diag = true;
10961096
for mut diag in diags.into_iter() {
1097-
diag.update_unstable_expectation_id(unstable_to_stable);
1097+
diag.update_unstable_expectation_id(&unstable_to_stable);
10981098

10991099
// Here the diagnostic is given back to `emit_diagnostic` where it was first
11001100
// intercepted. Now it should be processed as usual, since the unstable expectation
@@ -1106,11 +1106,11 @@ impl<'a> DiagCtxtHandle<'a> {
11061106
inner
11071107
.stashed_diagnostics
11081108
.values_mut()
1109-
.for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable));
1109+
.for_each(|(diag, _guar)| diag.update_unstable_expectation_id(&unstable_to_stable));
11101110
inner
11111111
.future_breakage_diagnostics
11121112
.iter_mut()
1113-
.for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable));
1113+
.for_each(|diag| diag.update_unstable_expectation_id(&unstable_to_stable));
11141114
}
11151115

11161116
/// This methods steals all [`LintExpectationId`]s that are stored inside
@@ -1556,7 +1556,7 @@ impl DiagCtxtInner {
15561556
if let LintExpectationId::Unstable { .. } = expect_id {
15571557
unreachable!(); // this case was handled at the top of this function
15581558
}
1559-
self.fulfilled_expectations.insert(expect_id.normalize());
1559+
self.fulfilled_expectations.insert(expect_id);
15601560
if let Expect(_) = diagnostic.level {
15611561
// Nothing emitted here for expected lints.
15621562
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);

compiler/rustc_lint/src/expect.rs

+49-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,65 @@
11
use crate::lints::{Expectation, ExpectationNote};
2+
use rustc_data_structures::fx::FxIndexMap;
3+
use rustc_hir::{HirId, CRATE_OWNER_ID};
4+
use rustc_middle::lint::LintExpectation;
25
use rustc_middle::query::Providers;
36
use rustc_middle::ty::TyCtxt;
47
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
5-
use rustc_session::lint::LintExpectationId;
8+
use rustc_session::lint::{Level, LintExpectationId};
69
use rustc_span::Symbol;
710

811
pub(crate) fn provide(providers: &mut Providers) {
9-
*providers = Providers { check_expectations, ..*providers };
12+
*providers = Providers { lint_expectations, check_expectations, ..*providers };
13+
}
14+
15+
fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
16+
let krate = tcx.hir_crate_items(());
17+
18+
let mut expectations = Vec::new();
19+
let mut unstable_to_stable_ids = FxIndexMap::default();
20+
21+
let mut record_stable = |attr_id, hir_id, attr_index| {
22+
let expect_id = LintExpectationId::Stable { hir_id, attr_index, lint_index: None };
23+
unstable_to_stable_ids.entry(attr_id).or_insert(expect_id);
24+
};
25+
let mut push_expectations = |owner| {
26+
let lints = tcx.shallow_lint_levels_on(owner);
27+
if lints.expectations.is_empty() {
28+
return;
29+
}
30+
31+
expectations.extend_from_slice(&lints.expectations);
32+
33+
let attrs = tcx.hir_attrs(owner);
34+
for &(local_id, attrs) in attrs.map.iter() {
35+
// Some attributes appear multiple times in HIR, to ensure they are correctly taken
36+
// into account where they matter. This means we cannot just associate the AttrId to
37+
// the first HirId where we see it, but need to check it actually appears in a lint
38+
// level.
39+
// FIXME(cjgillot): Can this cause an attribute to appear in multiple expectation ids?
40+
if !lints.specs.contains_key(&local_id) {
41+
continue;
42+
}
43+
for (attr_index, attr) in attrs.iter().enumerate() {
44+
let Some(Level::Expect(_)) = Level::from_attr(attr) else { continue };
45+
record_stable(attr.id, HirId { owner, local_id }, attr_index.try_into().unwrap());
46+
}
47+
}
48+
};
49+
50+
push_expectations(CRATE_OWNER_ID);
51+
for owner in krate.owners() {
52+
push_expectations(owner);
53+
}
54+
55+
tcx.dcx().update_unstable_expectation_id(unstable_to_stable_ids);
56+
expectations
1057
}
1158

1259
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
1360
let lint_expectations = tcx.lint_expectations(());
1461
let fulfilled_expectations = tcx.dcx().steal_fulfilled_expectation_ids();
1562

16-
tracing::debug!(?lint_expectations, ?fulfilled_expectations);
17-
1863
for (id, expectation) in lint_expectations {
1964
// This check will always be true, since `lint_expectations` only
2065
// holds stable ids

0 commit comments

Comments
 (0)