Skip to content

Commit 2ca9037

Browse files
committed
Set LintExpectationId in level and collect fulfilled ones (RFC-2383)
* Collect lint expectations and set expectation ID in level (RFC-2383) * Collect IDs of fulfilled lint expectations from diagnostics (RFC 2383)
1 parent f467a58 commit 2ca9037

File tree

7 files changed

+102
-19
lines changed

7 files changed

+102
-19
lines changed

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,7 @@ impl Diagnostic {
133133
| Level::Error { .. }
134134
| Level::FailureNote => true,
135135

136-
Level::Warning
137-
| Level::Note
138-
| Level::Help
139-
| Level::Allow
140-
| Level::Expect(_) => false,
136+
Level::Warning | Level::Note | Level::Help | Level::Allow | Level::Expect(_) => false,
141137
}
142138
}
143139

compiler/rustc_errors/src/lib.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,15 @@ struct HandlerInner {
451451
deduplicated_warn_count: usize,
452452

453453
future_breakage_diagnostics: Vec<Diagnostic>,
454+
455+
/// Lint [`Diagnostic`]s can be expected as described in [RFC-2383]. An
456+
/// expected diagnostic will have the level `Expect` which additionally
457+
/// carries the [`LintExpectationId`] of the expectation that can be
458+
/// marked as fulfilled. This is a collection of all [`LintExpectationId`]s
459+
/// that have been marked as fulfilled this way.
460+
///
461+
/// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
462+
fulfilled_expectations: FxHashSet<LintExpectationId>,
454463
}
455464

456465
/// A key denoting where from a diagnostic was stashed.
@@ -571,6 +580,7 @@ impl Handler {
571580
emitted_diagnostics: Default::default(),
572581
stashed_diagnostics: Default::default(),
573582
future_breakage_diagnostics: Vec::new(),
583+
fulfilled_expectations: Default::default(),
574584
}),
575585
}
576586
}
@@ -912,6 +922,12 @@ impl Handler {
912922
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
913923
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
914924
}
925+
926+
/// This methods steals all [`LintExpectationId`]s that are stored inside
927+
/// [`HandlerInner`] and indicate that the linked expectation has been fulfilled.
928+
pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet<LintExpectationId> {
929+
std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
930+
}
915931
}
916932

917933
impl HandlerInner {
@@ -959,7 +975,8 @@ impl HandlerInner {
959975

960976
(*TRACK_DIAGNOSTICS)(diagnostic);
961977

962-
if let Level::Expect(_) = diagnostic.level {
978+
if let Level::Expect(expectation_id) = diagnostic.level {
979+
self.fulfilled_expectations.insert(expectation_id);
963980
return;
964981
} else if diagnostic.level == Allow {
965982
return;

compiler/rustc_lint/src/levels.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic};
77
use rustc_hir as hir;
88
use rustc_hir::{intravisit, HirId};
99
use rustc_middle::hir::nested_filter;
10-
use rustc_middle::lint::LevelAndSource;
11-
use rustc_middle::lint::LintDiagnosticBuilder;
1210
use rustc_middle::lint::{
13-
struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, LintStackIndex,
14-
COMMAND_LINE,
11+
struct_lint_level, LevelAndSource, LintDiagnosticBuilder, LintExpectation, LintLevelMap,
12+
LintLevelSets, LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE,
1513
};
1614
use rustc_middle::ty::query::Providers;
1715
use rustc_middle::ty::{RegisteredTools, TyCtxt};
1816
use rustc_session::lint::{
1917
builtin::{self, FORBIDDEN_LINT_GROUPS},
20-
Level, Lint, LintId,
18+
Level, Lint, LintExpectationId, LintId,
2119
};
2220
use rustc_session::parse::feature_err;
2321
use rustc_session::Session;
@@ -44,6 +42,7 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
4442

4543
pub struct LintLevelsBuilder<'s> {
4644
sess: &'s Session,
45+
lint_expectations: FxHashMap<LintExpectationId, LintExpectation>,
4746
sets: LintLevelSets,
4847
id_to_set: FxHashMap<HirId, LintStackIndex>,
4948
cur: LintStackIndex,
@@ -66,6 +65,7 @@ impl<'s> LintLevelsBuilder<'s> {
6665
) -> Self {
6766
let mut builder = LintLevelsBuilder {
6867
sess,
68+
lint_expectations: Default::default(),
6969
sets: LintLevelSets::new(),
7070
cur: COMMAND_LINE,
7171
id_to_set: Default::default(),
@@ -231,7 +231,7 @@ impl<'s> LintLevelsBuilder<'s> {
231231
let sess = self.sess;
232232
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
233233
for attr in attrs {
234-
let Some(level) = Level::from_symbol(attr.name_or_empty()) else {
234+
let Some(level) = Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()) else {
235235
continue
236236
};
237237

@@ -476,6 +476,26 @@ impl<'s> LintLevelsBuilder<'s> {
476476
}
477477
}
478478
}
479+
480+
if !specs.is_empty() {
481+
// Only lints that are currently registered in the lint store
482+
// have been found and added to `specs`. Creating the expectation
483+
// here ensures that it can be fulfilled during this compilation
484+
// session.
485+
if let Level::Expect(expect_id) = level {
486+
let has_lints = specs
487+
.values()
488+
.any(|(lvl, _src)| matches!(lvl, Level::Expect(check_id) if check_id.eq(&expect_id)));
489+
490+
if has_lints {
491+
let lint = builtin::UNFULFILLED_LINT_EXPECTATIONS;
492+
let (lvl, src) =
493+
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
494+
let expectation = LintExpectation::new(reason, attr.span, lvl, src);
495+
self.lint_expectations.insert(expect_id, expectation);
496+
}
497+
}
498+
}
479499
}
480500

481501
if !is_crate_node {
@@ -563,7 +583,11 @@ impl<'s> LintLevelsBuilder<'s> {
563583
}
564584

565585
pub fn build_map(self) -> LintLevelMap {
566-
LintLevelMap { sets: self.sets, id_to_set: self.id_to_set }
586+
LintLevelMap {
587+
sets: self.sets,
588+
id_to_set: self.id_to_set,
589+
lint_expectations: self.lint_expectations,
590+
}
567591
}
568592
}
569593

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ impl Level {
133133
}
134134

135135
/// Converts a symbol to a level.
136-
pub fn from_symbol(x: Symbol) -> Option<Level> {
136+
pub fn from_symbol(x: Symbol, possible_lint_expect_id: u32) -> Option<Level> {
137137
match x {
138138
sym::allow => Some(Level::Allow),
139-
sym::expect => Some(Level::Expect(LintExpectationId::from(0u32))),
139+
sym::expect => Some(Level::Expect(LintExpectationId::from(possible_lint_expect_id))),
140140
sym::warn => Some(Level::Warn),
141141
sym::deny => Some(Level::Deny),
142142
sym::forbid => Some(Level::Forbid),

compiler/rustc_middle/src/lint.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId};
66
use rustc_hir::HirId;
77
use rustc_index::vec::IndexVec;
88
use rustc_query_system::ich::StableHashingContext;
9+
use rustc_session::lint::LintExpectationId;
910
use rustc_session::lint::{
1011
builtin::{self, FORBIDDEN_LINT_GROUPS},
1112
FutureIncompatibilityReason, Level, Lint, LintId,
@@ -153,6 +154,13 @@ impl LintLevelSets {
153154

154155
#[derive(Debug)]
155156
pub struct LintLevelMap {
157+
/// This is a collection of lint expectations as described in RFC 2383, that
158+
/// can be fulfilled during this compilation session. This means that at least
159+
/// one expected lint is currently registered in the lint store.
160+
///
161+
/// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect)
162+
/// lint level.
163+
pub lint_expectations: FxHashMap<LintExpectationId, LintExpectation>,
156164
pub sets: LintLevelSets,
157165
pub id_to_set: FxHashMap<HirId, LintStackIndex>,
158166
}
@@ -178,14 +186,42 @@ impl LintLevelMap {
178186
impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
179187
#[inline]
180188
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
181-
let LintLevelMap { ref sets, ref id_to_set } = *self;
189+
let LintLevelMap { ref sets, ref id_to_set, ref lint_expectations } = *self;
182190

183191
id_to_set.hash_stable(hcx, hasher);
192+
lint_expectations.hash_stable(hcx, hasher);
184193

185194
hcx.while_hashing_spans(true, |hcx| sets.hash_stable(hcx, hasher))
186195
}
187196
}
188197

198+
/// This struct represents a lint expectation and holds all required information
199+
/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after
200+
/// the `LateLintPass` has completed.
201+
#[derive(Clone, Debug, HashStable)]
202+
pub struct LintExpectation {
203+
/// The reason for this expectation that can optionally be added as part of
204+
/// the attribute. It will be displayed as part of the lint message.
205+
pub reason: Option<Symbol>,
206+
/// The [`Span`] of the attribute that this expectation originated from.
207+
pub emission_span: Span,
208+
/// The [`Level`] that this lint diagnostic should be emitted if unfulfilled.
209+
pub emission_level: Level,
210+
/// The [`LintLevelSource`] information needed for [`struct_lint_level`].
211+
pub emission_level_source: LintLevelSource,
212+
}
213+
214+
impl LintExpectation {
215+
pub fn new(
216+
reason: Option<Symbol>,
217+
attr_span: Span,
218+
emission_level: Level,
219+
emission_level_source: LintLevelSource,
220+
) -> Self {
221+
Self { reason, emission_span: attr_span, emission_level, emission_level_source }
222+
}
223+
}
224+
189225
pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a, ()>);
190226

191227
impl<'a> LintDiagnosticBuilder<'a> {
@@ -225,7 +261,9 @@ pub fn explain_lint_level_source(
225261
Level::Forbid => "-F",
226262
Level::Allow => "-A",
227263
Level::ForceWarn => "--force-warn",
228-
Level::Expect(_) => unreachable!("the expect level does not have a commandline flag"),
264+
Level::Expect(_) => {
265+
unreachable!("the expect level does not have a commandline flag")
266+
}
229267
};
230268
let hyphen_case_lint_name = name.replace('_', "-");
231269
if lint_flag_val.as_str() == name {

compiler/rustc_middle/src/ty/context.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2755,7 +2755,11 @@ impl<'tcx> TyCtxt<'tcx> {
27552755
return bound;
27562756
}
27572757

2758-
if hir.attrs(id).iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some()) {
2758+
if hir
2759+
.attrs(id)
2760+
.iter()
2761+
.any(|attr| Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()).is_some())
2762+
{
27592763
return id;
27602764
}
27612765
let next = hir.get_parent_node(id);

compiler/rustc_session/src/session.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,11 @@ impl Session {
331331
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
332332
self.diagnostic().struct_allow(msg)
333333
}
334-
pub fn struct_expect(&self, msg: &str, id: lint::LintExpectationId) -> DiagnosticBuilder<'_, ()> {
334+
pub fn struct_expect(
335+
&self,
336+
msg: &str,
337+
id: lint::LintExpectationId,
338+
) -> DiagnosticBuilder<'_, ()> {
335339
self.diagnostic().struct_expect(msg, id)
336340
}
337341
pub fn struct_span_err<S: Into<MultiSpan>>(

0 commit comments

Comments
 (0)