Skip to content

Commit aa2a0a8

Browse files
committed
Expect each lint in attribute individually (RFC-2383)
1 parent a9bf9ea commit aa2a0a8

File tree

12 files changed

+210
-103
lines changed

12 files changed

+210
-103
lines changed

compiler/rustc_errors/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -942,10 +942,16 @@ impl Handler {
942942

943943
let mut inner = self.inner.borrow_mut();
944944
for mut diag in diags.into_iter() {
945-
if let Some(unstable_id) = diag.level.get_expectation_id() {
946-
if let Some(stable_id) = unstable_to_stable.get(&unstable_id) {
947-
diag.level = Level::Expect(*stable_id);
948-
inner.fulfilled_expectations.insert(*stable_id);
945+
if let Some(mut unstable_id) = diag.level.get_expectation_id() {
946+
let lint_index = unstable_id.get_lint_index();
947+
948+
// The unstable to stable map only maps the unstable it to a stable id
949+
// the lint index is manually transferred here.
950+
unstable_id.set_lint_index(None);
951+
if let Some(mut stable_id) = unstable_to_stable.get(&unstable_id).map(|id| *id) {
952+
stable_id.set_lint_index(lint_index);
953+
diag.level = Level::Expect(stable_id);
954+
inner.fulfilled_expectations.insert(stable_id);
949955
}
950956
}
951957

@@ -1007,7 +1013,7 @@ impl HandlerInner {
10071013
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
10081014
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
10091015
// a stable one by the `LintLevelsBuilder`.
1010-
if let Level::Expect(LintExpectationId::Unstable(_)) = diagnostic.level {
1016+
if let Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level {
10111017
self.unstable_expect_diagnostics.push(diagnostic.clone());
10121018
return;
10131019
}

compiler/rustc_lint/src/levels.rs

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,7 @@ impl<'s> LintLevelsBuilder<'s> {
243243
let sess = self.sess;
244244
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
245245
for (attr_index, attr) in attrs.iter().enumerate() {
246-
let level = match Level::from_symbol(attr.name_or_empty(), || {
247-
LintExpectationId::Unstable(attr.id)
248-
}) {
246+
let level = match Level::from_attr(attr) {
249247
None => continue,
250248
Some(Level::Expect(unstable_id)) if source_hir_id.is_some() => {
251249
let stable_id =
@@ -305,9 +303,23 @@ impl<'s> LintLevelsBuilder<'s> {
305303
}
306304
}
307305

308-
for li in metas {
306+
let (unfulfilled_lint_lvl, unfulfilled_lint_src) = self.sets.get_lint_level(
307+
builtin::UNFULFILLED_LINT_EXPECTATIONS,
308+
self.cur,
309+
Some(&specs),
310+
&sess,
311+
);
312+
for (lint_index, li) in metas.iter_mut().enumerate() {
313+
let level = match level {
314+
Level::Expect(mut id) => {
315+
id.set_lint_index(Some(lint_index));
316+
Level::Expect(id)
317+
}
318+
level => level,
319+
};
320+
309321
let sp = li.span();
310-
let mut meta_item = match li {
322+
let meta_item = match li {
311323
ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
312324
_ => {
313325
let mut err = bad_attr(sp);
@@ -347,6 +359,17 @@ impl<'s> LintLevelsBuilder<'s> {
347359
self.check_gated_lint(id, attr.span);
348360
self.insert_spec(&mut specs, id, (level, src));
349361
}
362+
if let Level::Expect(expect_id) = level {
363+
self.lint_expectations.insert(
364+
expect_id,
365+
LintExpectation::new(
366+
reason,
367+
sp,
368+
unfulfilled_lint_lvl,
369+
unfulfilled_lint_src,
370+
),
371+
);
372+
}
350373
}
351374

352375
CheckLintNameResult::Tool(result) => {
@@ -362,6 +385,17 @@ impl<'s> LintLevelsBuilder<'s> {
362385
for id in ids {
363386
self.insert_spec(&mut specs, *id, (level, src));
364387
}
388+
if let Level::Expect(expect_id) = level {
389+
self.lint_expectations.insert(
390+
expect_id,
391+
LintExpectation::new(
392+
reason,
393+
sp,
394+
unfulfilled_lint_lvl,
395+
unfulfilled_lint_src,
396+
),
397+
);
398+
}
365399
}
366400
Err((Some(ids), ref new_lint_name)) => {
367401
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
@@ -398,6 +432,17 @@ impl<'s> LintLevelsBuilder<'s> {
398432
for id in ids {
399433
self.insert_spec(&mut specs, *id, (level, src));
400434
}
435+
if let Level::Expect(expect_id) = level {
436+
self.lint_expectations.insert(
437+
expect_id,
438+
LintExpectation::new(
439+
reason,
440+
sp,
441+
unfulfilled_lint_lvl,
442+
unfulfilled_lint_src,
443+
),
444+
);
445+
}
401446
}
402447
Err((None, _)) => {
403448
// If Tool(Err(None, _)) is returned, then either the lint does not
@@ -491,31 +536,22 @@ impl<'s> LintLevelsBuilder<'s> {
491536
self.check_gated_lint(id, attr.span);
492537
self.insert_spec(&mut specs, id, (level, src));
493538
}
539+
if let Level::Expect(expect_id) = level {
540+
self.lint_expectations.insert(
541+
expect_id,
542+
LintExpectation::new(
543+
reason,
544+
sp,
545+
unfulfilled_lint_lvl,
546+
unfulfilled_lint_src,
547+
),
548+
);
549+
}
494550
} else {
495551
panic!("renamed lint does not exist: {}", new_name);
496552
}
497553
}
498554
}
499-
500-
if !specs.is_empty() {
501-
// Only lints that are currently registered in the lint store
502-
// have been found and added to `specs`. Creating the expectation
503-
// here ensures that it can be fulfilled during this compilation
504-
// session.
505-
if let Level::Expect(expect_id) = level {
506-
let has_lints = specs
507-
.values()
508-
.any(|(lvl, _src)| matches!(lvl, Level::Expect(check_id) if check_id.eq(&expect_id)));
509-
510-
if has_lints {
511-
let lint = builtin::UNFULFILLED_LINT_EXPECTATIONS;
512-
let (lvl, src) =
513-
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
514-
let expectation = LintExpectation::new(reason, attr.span, lvl, src);
515-
self.lint_expectations.insert(expect_id, expectation);
516-
}
517-
}
518-
}
519555
}
520556

521557
if !is_crate_node {
@@ -565,7 +601,7 @@ impl<'s> LintLevelsBuilder<'s> {
565601
hir_id: HirId,
566602
attr_index: usize,
567603
) -> LintExpectationId {
568-
let stable_id = LintExpectationId::Stable { hir_id, attr_index };
604+
let stable_id = LintExpectationId::Stable { hir_id, attr_index, lint_index: None };
569605

570606
self.expectation_id_map.insert(unstable_id, stable_id);
571607

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ extern crate rustc_macros;
55

66
pub use self::Level::*;
77
use rustc_ast::node_id::{NodeId, NodeMap};
8-
use rustc_ast::AttrId;
8+
use rustc_ast::{AttrId, Attribute};
99
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
1010
use rustc_hir::HirId;
1111
use rustc_serialize::json::Json;
@@ -63,56 +63,72 @@ pub enum Applicability {
6363
/// These `LintExpectationId` will be updated to use the stable [`HirId`] once the
6464
/// AST has been lowered. The transformation is done by the
6565
/// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`]
66+
///
67+
/// Each lint inside the `expect` attribute is tracked individually, the `lint_index`
68+
/// identifies the lint inside the attribute and ensures that the IDs are unique.
6669
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)]
6770
pub enum LintExpectationId {
6871
/// Used for lints emitted during the `EarlyLintPass`. This id is not
6972
/// has stable and should not be cached.
70-
Unstable(AttrId),
73+
Unstable { attr_id: AttrId, lint_index: Option<usize> },
7174
/// The [`HirId`] that the lint expectation is attached to. This id is
7275
/// stable and can be cached. The additional index ensures that nodes with
7376
/// several expectations can correctly match diagnostics to the individual
7477
/// expectation.
75-
Stable { hir_id: HirId, attr_index: usize },
78+
Stable { hir_id: HirId, attr_index: usize, lint_index: Option<usize> },
7679
}
7780

7881
impl LintExpectationId {
7982
pub fn is_stable(&self) -> bool {
8083
match self {
81-
LintExpectationId::Unstable(_) => false,
84+
LintExpectationId::Unstable { .. } => false,
8285
LintExpectationId::Stable { .. } => true,
8386
}
8487
}
88+
89+
pub fn get_lint_index(&self) -> Option<usize> {
90+
let (LintExpectationId::Unstable { lint_index, .. }
91+
| LintExpectationId::Stable { lint_index, .. }) = self;
92+
93+
*lint_index
94+
}
95+
96+
pub fn set_lint_index(&mut self, new_lint_index: Option<usize>) {
97+
let (LintExpectationId::Unstable { ref mut lint_index, .. }
98+
| LintExpectationId::Stable { ref mut lint_index, .. }) = self;
99+
100+
*lint_index = new_lint_index
101+
}
85102
}
86103

87104
impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
88105
#[inline]
89106
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
90107
match self {
91-
LintExpectationId::Unstable(_) => {
92-
unreachable!(
93-
"HashStable should never be called for an unstable `LintExpectationId`"
94-
)
95-
}
96-
LintExpectationId::Stable { hir_id, attr_index } => {
108+
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
97109
hir_id.hash_stable(hcx, hasher);
98110
attr_index.hash_stable(hcx, hasher);
111+
lint_index.hash_stable(hcx, hasher);
112+
}
113+
_ => {
114+
unreachable!("HashStable should only be called for a filled `LintExpectationId`")
99115
}
100116
}
101117
}
102118
}
103119

104120
impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
105-
type KeyType = (HirId, usize);
121+
type KeyType = (HirId, usize, usize);
106122

107123
#[inline]
108124
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
109125
match self {
110-
LintExpectationId::Unstable(_) => {
111-
unreachable!(
112-
"HashStable should never be called for an unstable `LintExpectationId`"
113-
)
126+
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
127+
(*hir_id, *attr_index, *lint_index)
128+
}
129+
_ => {
130+
unreachable!("HashStable should only be called for a filled `LintExpectationId`")
114131
}
115-
LintExpectationId::Stable { hir_id, attr_index } => (*hir_id, *attr_index),
116132
}
117133
}
118134
}
@@ -176,13 +192,13 @@ impl Level {
176192
}
177193

178194
/// Converts a symbol to a level.
179-
pub fn from_symbol<F>(x: Symbol, create_expectation_id: F) -> Option<Level>
180-
where
181-
F: FnOnce() -> LintExpectationId,
182-
{
183-
match x {
195+
pub fn from_attr(attr: &Attribute) -> Option<Level> {
196+
match attr.name_or_empty() {
184197
sym::allow => Some(Level::Allow),
185-
sym::expect => Some(Level::Expect(create_expectation_id())),
198+
sym::expect => Some(Level::Expect(LintExpectationId::Unstable {
199+
attr_id: attr.id,
200+
lint_index: None,
201+
})),
186202
sym::warn => Some(Level::Warn),
187203
sym::deny => Some(Level::Deny),
188204
sym::forbid => Some(Level::Forbid),

compiler/rustc_middle/src/ty/context.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use rustc_middle::mir::FakeReadCause;
4949
use rustc_query_system::ich::{NodeIdHashingMode, StableHashingContext};
5050
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
5151
use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames};
52-
use rustc_session::lint::{Level, Lint, LintExpectationId};
52+
use rustc_session::lint::{Level, Lint};
5353
use rustc_session::Limit;
5454
use rustc_session::Session;
5555
use rustc_span::def_id::{DefPathHash, StableCrateId};
@@ -2755,13 +2755,7 @@ impl<'tcx> TyCtxt<'tcx> {
27552755
return bound;
27562756
}
27572757

2758-
if hir.attrs(id).iter().enumerate().any(|(attr_index, attr)| {
2759-
Level::from_symbol(attr.name_or_empty(), || LintExpectationId::Stable {
2760-
hir_id: id,
2761-
attr_index,
2762-
})
2763-
.is_some()
2764-
}) {
2758+
if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) {
27652759
return id;
27662760
}
27672761
let next = hir.get_parent_node(id);

src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
warning: this lint expectation is unfulfilled
2-
--> $DIR/crate_level_expect.rs:7:1
2+
--> $DIR/crate_level_expect.rs:7:11
33
|
44
LL | #![expect(unused_mut)]
5-
| ^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^
66
|
77
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
88

src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,55 @@
44

55
#![warn(unused)]
66

7+
// The warnings are not double triggers, they identify different unfulfilled lint
8+
// expectations one for each listed lint.
9+
710
#[expect(unused_variables, unused_mut, while_true)]
11+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
12+
//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default
13+
//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
814
fn check_multiple_lints_1() {
915
// This only trigger `unused_variables`
1016
let who_am_i = 666;
1117
}
1218

1319
#[expect(unused_variables, unused_mut, while_true)]
20+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
21+
//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
1422
fn check_multiple_lints_2() {
1523
// This only triggers `unused_mut`
1624
let mut x = 0;
1725
println!("I use x: {}", x);
1826
}
1927

20-
2128
#[expect(unused_variables, unused_mut, while_true)]
29+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
30+
//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
2231
fn check_multiple_lints_3() {
2332
// This only triggers `while_true` which is also an early lint
2433
while true {}
2534
}
2635

36+
#[expect(unused, while_true)]
37+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
38+
fn check_multiple_lints_with_lint_group_1() {
39+
let who_am_i = 666;
40+
41+
let mut x = 0;
42+
println!("I use x: {}", x);
43+
}
44+
45+
#[expect(unused, while_true)]
46+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
47+
fn check_multiple_lints_with_lint_group_2() {
48+
while true {}
49+
}
50+
2751
fn main() {
2852
check_multiple_lints_1();
2953
check_multiple_lints_2();
3054
check_multiple_lints_3();
55+
56+
check_multiple_lints_with_lint_group_1();
57+
check_multiple_lints_with_lint_group_2();
3158
}

0 commit comments

Comments
 (0)