Skip to content

Commit 243029b

Browse files
committed
Added expect lint level and attribute (RFC-2383)
1 parent a985d8e commit 243029b

File tree

11 files changed

+98
-2
lines changed

11 files changed

+98
-2
lines changed

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
7373
// FIXME(#59346): Not sure how to map these two levels
7474
Level::Cancelled | Level::FailureNote => AnnotationType::Error,
7575
Level::Allow => panic!("Should not call with Allow"),
76+
Level::Expect => panic!("Should not call with Expect"),
7677
}
7778
}
7879

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,12 @@ impl Diagnostic {
9898
match self.level {
9999
Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
100100

101-
Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false,
101+
Level::Warning
102+
| Level::Note
103+
| Level::Help
104+
| Level::Cancelled
105+
| Level::Allow
106+
| Level::Expect => false,
102107
}
103108
}
104109

compiler/rustc_errors/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,11 @@ impl Handler {
589589
DiagnosticBuilder::new(self, Level::Allow, msg)
590590
}
591591

592+
/// Construct a builder at the `Expect` level with the `msg`.
593+
pub fn struct_expect(&self, msg: &str) -> DiagnosticBuilder<'_> {
594+
DiagnosticBuilder::new(self, Level::Expect, msg)
595+
}
596+
592597
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
593598
pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
594599
let mut result = self.struct_err(msg);
@@ -1101,6 +1106,7 @@ pub enum Level {
11011106
Cancelled,
11021107
FailureNote,
11031108
Allow,
1109+
Expect,
11041110
}
11051111

11061112
impl fmt::Display for Level {
@@ -1126,7 +1132,7 @@ impl Level {
11261132
spec.set_fg(Some(Color::Cyan)).set_intense(true);
11271133
}
11281134
FailureNote => {}
1129-
Allow | Cancelled => unreachable!(),
1135+
Allow | Expect | Cancelled => unreachable!(),
11301136
}
11311137
spec
11321138
}
@@ -1141,6 +1147,7 @@ impl Level {
11411147
FailureNote => "failure-note",
11421148
Cancelled => panic!("Shouldn't call on cancelled error"),
11431149
Allow => panic!("Shouldn't call on allowed error"),
1150+
Expect => panic!("Shouldn't call on expected error"),
11441151
}
11451152
}
11461153

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
204204
// Lints:
205205
ungated!(warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
206206
ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
207+
gated!(
208+
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), lint_reasons,
209+
experimental!(expect)
210+
),
207211
ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
208212
ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
209213
ungated!(must_use, AssumedUsed, template!(Word, NameValueStr: "reason")),

compiler/rustc_lint/src/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ struct LintGroup {
125125
depr: Option<LintAlias>,
126126
}
127127

128+
#[derive(Debug)]
128129
pub enum CheckLintNameResult<'a> {
129130
Ok(&'a [LintId]),
130131
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
@@ -373,6 +374,9 @@ impl LintStore {
373374
Level::ForceWarn => "--force-warn",
374375
Level::Deny => "-D",
375376
Level::Forbid => "-F",
377+
Level::Expect => {
378+
unreachable!("lints with the level of `expect` should not run this code");
379+
}
376380
},
377381
lint_name
378382
);

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,31 @@ pub enum Applicability {
4747
}
4848

4949
/// Setting for how to handle a lint.
50+
///
51+
/// See: https://doc.rust-lang.org/rustc/lints/levels.html
5052
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
5153
pub enum Level {
54+
/// The `allow` level will not issue any message.
5255
Allow,
56+
/// The `expect` level will suppress the lint message but intern produce a message
57+
/// if the lint wasn't issued in the expected scope. Expect should not be used as
58+
/// an initial level for a lint.
59+
///
60+
/// Note that this still means that the lint is enabled in this position and should
61+
/// be passed onwards to the `LintContext` which will fulfill the expectation and
62+
/// suppress the lint.
63+
///
64+
/// See RFC 2383.
65+
Expect,
66+
/// The `warn` level will produce a warning if the lint was violated, however the
67+
/// compiler will continue with its execution.
5368
Warn,
5469
ForceWarn,
70+
/// The `deny` level will produce an error and stop further execution after the lint
71+
/// pass is complete.
5572
Deny,
73+
/// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous
74+
/// levels.
5675
Forbid,
5776
}
5877

@@ -63,6 +82,7 @@ impl Level {
6382
pub fn as_str(self) -> &'static str {
6483
match self {
6584
Level::Allow => "allow",
85+
Level::Expect => "expect",
6686
Level::Warn => "warn",
6787
Level::ForceWarn => "force-warn",
6888
Level::Deny => "deny",
@@ -74,6 +94,7 @@ impl Level {
7494
pub fn from_str(x: &str) -> Option<Level> {
7595
match x {
7696
"allow" => Some(Level::Allow),
97+
"expect" => Some(Level::Expect),
7798
"warn" => Some(Level::Warn),
7899
"deny" => Some(Level::Deny),
79100
"forbid" => Some(Level::Forbid),
@@ -85,6 +106,7 @@ impl Level {
85106
pub fn from_symbol(x: Symbol) -> Option<Level> {
86107
match x {
87108
sym::allow => Some(Level::Allow),
109+
sym::expect => Some(Level::Expect),
88110
sym::warn => Some(Level::Warn),
89111
sym::deny => Some(Level::Deny),
90112
sym::forbid => Some(Level::Forbid),

compiler/rustc_middle/src/lint.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ pub fn struct_lint_level<'s, 'd>(
243243
return;
244244
}
245245
}
246+
(Level::Expect, _) => {
247+
// This case is special as we actually allow the lint itself in this context, but
248+
// we can't return early like in the case for `Level::Allow` because we still
249+
// need the lint diagnostic to be emitted to `rustc_error::HanderInner`.
250+
//
251+
// We can also not save the diagnostic here right away as it could for instance
252+
// still be cancelled in the decorate closure. All of this means that we simply
253+
// create a `DiagnosticBuilder` and continue as we would for warnings.
254+
sess.struct_expect("")
255+
}
246256
(Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
247257
(Level::Warn, None) => sess.struct_warn(""),
248258
(Level::ForceWarn, Some(span)) => sess.struct_span_force_warn(span, ""),
@@ -274,6 +284,17 @@ pub fn struct_lint_level<'s, 'd>(
274284
}
275285

276286
let name = lint.name_lower();
287+
288+
// Lint diagnostics that are covered by the expect level will not be emitted outside
289+
// the compiler. It is therefore not necessary to add any information for the user.
290+
// This will therefore directly call the decorate function which will intern emit
291+
// the `Diagnostic`.
292+
if level == Level::Expect {
293+
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false });
294+
decorate(LintDiagnosticBuilder::new(err));
295+
return;
296+
}
297+
277298
match src {
278299
LintLevelSource::Default => {
279300
sess.diag_note_once(
@@ -289,6 +310,9 @@ pub fn struct_lint_level<'s, 'd>(
289310
Level::Forbid => "-F",
290311
Level::Allow => "-A",
291312
Level::ForceWarn => "--force-warn",
313+
Level::Expect => {
314+
unreachable!("lints with the level of `expect` should not run this code");
315+
}
292316
};
293317
let hyphen_case_lint_name = name.replace("_", "-");
294318
if lint_flag_val.as_str() == name {

compiler/rustc_session/src/session.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ impl Session {
360360
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
361361
self.diagnostic().struct_allow(msg)
362362
}
363+
pub fn struct_expect(&self, msg: &str) -> DiagnosticBuilder<'_> {
364+
self.diagnostic().struct_expect(msg)
365+
}
363366
pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
364367
self.diagnostic().struct_span_err(sp, msg)
365368
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// check-pass
2+
3+
#![feature(lint_reasons)]
4+
5+
// should be fine due to the enabled feature gate
6+
#[expect(unused_variables)]
7+
fn main() {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// should error due to missing feature gate.
2+
3+
#[expect(unused)]
4+
//~^ ERROR: the `#[expect]` attribute is an experimental feature [E0658]
5+
fn main() {
6+
let x = 1;
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: the `#[expect]` attribute is an experimental feature
2+
--> $DIR/expect_missing_feature_gate.rs:3:1
3+
|
4+
LL | #[expect(unused)]
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #54503 <https://github.com/rust-lang/rust/issues/54503> for more information
8+
= help: add `#![feature(lint_reasons)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)