Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 9fef3d9

Browse files
committed
Added Expect lint level and attribute (RFC-2383)
* Also added the `LintExpectationId` which will be used in future commits
1 parent c42d846 commit 9fef3d9

File tree

10 files changed

+107
-5
lines changed

10 files changed

+107
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,6 +3882,7 @@ version = "0.0.0"
38823882
dependencies = [
38833883
"rustc_ast",
38843884
"rustc_data_structures",
3885+
"rustc_index",
38853886
"rustc_macros",
38863887
"rustc_serialize",
38873888
"rustc_span",

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
7575
// FIXME(#59346): Not sure how to map this level
7676
Level::FailureNote => AnnotationType::Error,
7777
Level::Allow => panic!("Should not call with Allow"),
78+
Level::Expect(_) => panic!("Should not call with Expect"),
7879
}
7980
}
8081

compiler/rustc_errors/src/diagnostic.rs

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

136-
Level::Warning | Level::Note | Level::Help | Level::Allow => false,
136+
Level::Warning
137+
| Level::Note
138+
| Level::Help
139+
| Level::Allow
140+
| Level::Expect(_) => false,
137141
}
138142
}
139143

compiler/rustc_errors/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extern crate tracing;
2020

2121
pub use emitter::ColorConfig;
2222

23+
use rustc_lint_defs::LintExpectationId;
2324
use Level::*;
2425

2526
use emitter::{is_case_difference, Emitter, EmitterWriter};
@@ -677,6 +678,11 @@ impl Handler {
677678
DiagnosticBuilder::new(self, Level::Allow, msg)
678679
}
679680

681+
/// Construct a builder at the `Expect` level with the `msg`.
682+
pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> {
683+
DiagnosticBuilder::new(self, Level::Expect(id), msg)
684+
}
685+
680686
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
681687
pub fn struct_span_err(
682688
&self,
@@ -953,7 +959,9 @@ impl HandlerInner {
953959

954960
(*TRACK_DIAGNOSTICS)(diagnostic);
955961

956-
if diagnostic.level == Allow {
962+
if let Level::Expect(_) = diagnostic.level {
963+
return;
964+
} else if diagnostic.level == Allow {
957965
return;
958966
}
959967

@@ -1250,6 +1258,7 @@ pub enum Level {
12501258
Help,
12511259
FailureNote,
12521260
Allow,
1261+
Expect(LintExpectationId),
12531262
}
12541263

12551264
impl fmt::Display for Level {
@@ -1275,7 +1284,7 @@ impl Level {
12751284
spec.set_fg(Some(Color::Cyan)).set_intense(true);
12761285
}
12771286
FailureNote => {}
1278-
Allow => unreachable!(),
1287+
Allow | Expect(_) => unreachable!(),
12791288
}
12801289
spec
12811290
}
@@ -1289,6 +1298,7 @@ impl Level {
12891298
Help => "help",
12901299
FailureNote => "failure-note",
12911300
Allow => panic!("Shouldn't call on allowed error"),
1301+
Expect(_) => panic!("Shouldn't call on expected error"),
12921302
}
12931303
}
12941304

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
282282
ungated!(
283283
allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
284284
),
285+
gated!(
286+
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
287+
lint_reasons, experimental!(expect)
288+
),
285289
ungated!(
286290
forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
287291
),

compiler/rustc_lint/src/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct LintGroup {
109109
depr: Option<LintAlias>,
110110
}
111111

112+
#[derive(Debug)]
112113
pub enum CheckLintNameResult<'a> {
113114
Ok(&'a [LintId]),
114115
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
@@ -377,6 +378,9 @@ impl LintStore {
377378
Level::ForceWarn => "--force-warn",
378379
Level::Deny => "-D",
379380
Level::Forbid => "-F",
381+
Level::Expect(_) => {
382+
unreachable!("lints with the level of `expect` should not run this code");
383+
}
380384
},
381385
lint_name
382386
);

compiler/rustc_lint_defs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ rustc_span = { path = "../rustc_span" }
1010
rustc_serialize = { path = "../rustc_serialize" }
1111
rustc_macros = { path = "../rustc_macros" }
1212
rustc_target = { path = "../rustc_target" }
13+
rustc_index = { path = "../rustc_index" }

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![feature(min_specialization)]
2+
13
#[macro_use]
24
extern crate rustc_macros;
35

@@ -46,13 +48,60 @@ pub enum Applicability {
4648
Unspecified,
4749
}
4850

51+
rustc_index::newtype_index! {
52+
/// FIXME: The lint expectation ID is currently a simple copy of the `AttrId`
53+
/// that the expectation originated from. In the future it should be generated
54+
/// by other means. This is for one to keep the IDs independent of each other
55+
/// and also to ensure that it is actually stable between compilation sessions.
56+
/// (The `AttrId` for instance, is not stable).
57+
///
58+
/// Additionally, it would be nice if this generation could be moved into
59+
/// [`Level::from_symbol`] to have it all contained in one module and to
60+
/// make it simpler to use.
61+
pub struct LintExpectationId {
62+
DEBUG_FORMAT = "LintExpectationId({})"
63+
}
64+
}
65+
66+
rustc_data_structures::impl_stable_hash_via_hash!(LintExpectationId);
67+
68+
impl<HCX> ToStableHashKey<HCX> for LintExpectationId {
69+
type KeyType = u32;
70+
71+
#[inline]
72+
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
73+
self.as_u32()
74+
}
75+
}
76+
4977
/// Setting for how to handle a lint.
78+
///
79+
/// See: https://doc.rust-lang.org/rustc/lints/levels.html
5080
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
5181
pub enum Level {
82+
/// The `allow` level will not issue any message.
5283
Allow,
84+
/// The `expect` level will suppress the lint message but intern produce a message
85+
/// if the lint wasn't issued in the expected scope. `Expect` should not be used as
86+
/// an initial level for a lint.
87+
///
88+
/// Note that this still means that the lint is enabled in this position and should
89+
/// be emitted, this will intern fulfill the expectation and suppress the lint.
90+
///
91+
/// See RFC 2383.
92+
///
93+
/// The `LintExpectationId` is used to later link a lint emission to the actual
94+
/// expectation. It can be ignored in most cases.
95+
Expect(LintExpectationId),
96+
/// The `warn` level will produce a warning if the lint was violated, however the
97+
/// compiler will continue with its execution.
5398
Warn,
5499
ForceWarn,
100+
/// The `deny` level will produce an error and stop further execution after the lint
101+
/// pass is complete.
55102
Deny,
103+
/// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous
104+
/// levels.
56105
Forbid,
57106
}
58107

@@ -63,28 +112,31 @@ impl Level {
63112
pub fn as_str(self) -> &'static str {
64113
match self {
65114
Level::Allow => "allow",
115+
Level::Expect(_) => "expect",
66116
Level::Warn => "warn",
67117
Level::ForceWarn => "force-warn",
68118
Level::Deny => "deny",
69119
Level::Forbid => "forbid",
70120
}
71121
}
72122

73-
/// Converts a lower-case string to a level.
123+
/// Converts a lower-case string to a level. This will never construct the expect
124+
/// level as that would require a [`LintExpectationId`]
74125
pub fn from_str(x: &str) -> Option<Level> {
75126
match x {
76127
"allow" => Some(Level::Allow),
77128
"warn" => Some(Level::Warn),
78129
"deny" => Some(Level::Deny),
79130
"forbid" => Some(Level::Forbid),
80-
_ => None,
131+
"expect" | _ => None,
81132
}
82133
}
83134

84135
/// Converts a symbol to a level.
85136
pub fn from_symbol(x: Symbol) -> Option<Level> {
86137
match x {
87138
sym::allow => Some(Level::Allow),
139+
sym::expect => Some(Level::Expect(LintExpectationId::from(0u32))),
88140
sym::warn => Some(Level::Warn),
89141
sym::deny => Some(Level::Deny),
90142
sym::forbid => Some(Level::Forbid),

compiler/rustc_middle/src/lint.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ pub fn explain_lint_level_source(
225225
Level::Forbid => "-F",
226226
Level::Allow => "-A",
227227
Level::ForceWarn => "--force-warn",
228+
Level::Expect(_) => unreachable!("the expect level does not have a commandline flag"),
228229
};
229230
let hyphen_case_lint_name = name.replace('_', "-");
230231
if lint_flag_val.as_str() == name {
@@ -314,6 +315,16 @@ pub fn struct_lint_level<'s, 'd>(
314315
return;
315316
}
316317
}
318+
(Level::Expect(expect_id), _) => {
319+
// This case is special as we actually allow the lint itself in this context, but
320+
// we can't return early like in the case for `Level::Allow` because we still
321+
// need the lint diagnostic to be emitted to `rustc_error::HanderInner`.
322+
//
323+
// We can also not mark the lint expectation as fulfilled here right away, as it
324+
// can still be cancelled in the decorate function. All of this means that we simply
325+
// create a `DiagnosticBuilder` and continue as we would for warnings.
326+
sess.struct_expect("", expect_id)
327+
}
317328
(Level::Warn | Level::ForceWarn, Some(span)) => sess.struct_span_warn(span, ""),
318329
(Level::Warn | Level::ForceWarn, None) => sess.struct_warn(""),
319330
(Level::Deny | Level::Forbid, Some(span)) => {
@@ -346,6 +357,17 @@ pub fn struct_lint_level<'s, 'd>(
346357
}
347358
}
348359

360+
// Lint diagnostics that are covered by the expect level will not be emitted outside
361+
// the compiler. It is therefore not necessary to add any information for the user.
362+
// This will therefore directly call the decorate function which will intern emit
363+
// the `Diagnostic`.
364+
if let Level::Expect(_) = level {
365+
let name = lint.name_lower();
366+
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false });
367+
decorate(LintDiagnosticBuilder::new(err));
368+
return;
369+
}
370+
349371
explain_lint_level_source(sess, lint, level, src, &mut err);
350372

351373
let name = lint.name_lower();

compiler/rustc_session/src/session.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ 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<'_, ()> {
335+
self.diagnostic().struct_expect(msg, id)
336+
}
334337
pub fn struct_span_err<S: Into<MultiSpan>>(
335338
&self,
336339
sp: S,

0 commit comments

Comments
 (0)