Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8527a3d

Browse files
committedJun 16, 2022
Support lint expectations for --force-warn lints (RFC 2383)
1 parent ec55c61 commit 8527a3d

File tree

19 files changed

+317
-71
lines changed

19 files changed

+317
-71
lines changed
 

‎compiler/rustc_codegen_llvm/src/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ fn report_inline_asm(
340340
}
341341
let level = match level {
342342
llvm::DiagnosticLevel::Error => Level::Error { lint: false },
343-
llvm::DiagnosticLevel::Warning => Level::Warning,
343+
llvm::DiagnosticLevel::Warning => Level::Warning(None),
344344
llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
345345
};
346346
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);

‎compiler/rustc_codegen_ssa/src/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1761,7 +1761,7 @@ impl SharedEmitterMain {
17611761

17621762
let mut err = match level {
17631763
Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
1764-
Level::Warning => sess.struct_warn(msg),
1764+
Level::Warning(_) => sess.struct_warn(msg),
17651765
Level::Note => sess.struct_note_without_error(msg),
17661766
_ => bug!("Invalid inline asm diagnostic level"),
17671767
};

‎compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
8787
Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
8888
AnnotationType::Error
8989
}
90-
Level::Warning => AnnotationType::Warning,
90+
Level::Warning(_) => AnnotationType::Warning,
9191
Level::Note | Level::OnceNote => AnnotationType::Note,
9292
Level::Help => AnnotationType::Help,
9393
// FIXME(#59346): Not sure how to map this level

‎compiler/rustc_errors/src/diagnostic.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl Diagnostic {
208208
| Level::Error { .. }
209209
| Level::FailureNote => true,
210210

211-
Level::Warning
211+
Level::Warning(_)
212212
| Level::Note
213213
| Level::OnceNote
214214
| Level::Help
@@ -221,7 +221,9 @@ impl Diagnostic {
221221
&mut self,
222222
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
223223
) {
224-
if let Level::Expect(expectation_id) = &mut self.level {
224+
if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
225+
&mut self.level
226+
{
225227
if expectation_id.is_stable() {
226228
return;
227229
}
@@ -445,7 +447,7 @@ impl Diagnostic {
445447

446448
/// Add a warning attached to this diagnostic.
447449
pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
448-
self.sub(Level::Warning, msg, MultiSpan::new(), None);
450+
self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
449451
self
450452
}
451453

@@ -456,7 +458,7 @@ impl Diagnostic {
456458
sp: S,
457459
msg: impl Into<SubdiagnosticMessage>,
458460
) -> &mut Self {
459-
self.sub(Level::Warning, msg, sp.into(), None);
461+
self.sub(Level::Warning(None), msg, sp.into(), None);
460462
self
461463
}
462464

‎compiler/rustc_errors/src/json.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl Emitter for JsonEmitter {
154154
.into_iter()
155155
.map(|mut diag| {
156156
if diag.level == crate::Level::Allow {
157-
diag.level = crate::Level::Warning;
157+
diag.level = crate::Level::Warning(None);
158158
}
159159
FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) }
160160
})

‎compiler/rustc_errors/src/lib.rs

Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,23 @@ impl Handler {
658658
result
659659
}
660660

661+
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
662+
/// The `id` is used for lint emissions which should also fulfill a lint expectation.
663+
///
664+
/// Attempting to `.emit()` the builder will only emit if either:
665+
/// * `can_emit_warnings` is `true`
666+
/// * `is_force_warn` was set in `DiagnosticId::Lint`
667+
pub fn struct_span_warn_with_expectation(
668+
&self,
669+
span: impl Into<MultiSpan>,
670+
msg: impl Into<DiagnosticMessage>,
671+
id: LintExpectationId,
672+
) -> DiagnosticBuilder<'_, ()> {
673+
let mut result = self.struct_warn_with_expectation(msg, id);
674+
result.set_span(span);
675+
result
676+
}
677+
661678
/// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
662679
pub fn struct_span_allow(
663680
&self,
@@ -688,7 +705,21 @@ impl Handler {
688705
/// * `can_emit_warnings` is `true`
689706
/// * `is_force_warn` was set in `DiagnosticId::Lint`
690707
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
691-
DiagnosticBuilder::new(self, Level::Warning, msg)
708+
DiagnosticBuilder::new(self, Level::Warning(None), msg)
709+
}
710+
711+
/// Construct a builder at the `Warning` level with the `msg`. The `id` is used for
712+
/// lint emissions which should also fulfill a lint expectation.
713+
///
714+
/// Attempting to `.emit()` the builder will only emit if either:
715+
/// * `can_emit_warnings` is `true`
716+
/// * `is_force_warn` was set in `DiagnosticId::Lint`
717+
pub fn struct_warn_with_expectation(
718+
&self,
719+
msg: impl Into<DiagnosticMessage>,
720+
id: LintExpectationId,
721+
) -> DiagnosticBuilder<'_, ()> {
722+
DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg)
692723
}
693724

694725
/// Construct a builder at the `Allow` level with the `msg`.
@@ -842,7 +873,7 @@ impl Handler {
842873
}
843874

844875
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
845-
self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
876+
self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span);
846877
}
847878

848879
pub fn span_warn_with_code(
@@ -851,7 +882,7 @@ impl Handler {
851882
msg: impl Into<DiagnosticMessage>,
852883
code: DiagnosticId,
853884
) {
854-
self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span);
885+
self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span);
855886
}
856887

857888
pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
@@ -905,7 +936,7 @@ impl Handler {
905936
}
906937

907938
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
908-
let mut db = DiagnosticBuilder::new(self, Warning, msg);
939+
let mut db = DiagnosticBuilder::new(self, Warning(None), msg);
909940
db.emit();
910941
}
911942

@@ -1010,13 +1041,10 @@ impl Handler {
10101041
for mut diag in diags.into_iter() {
10111042
diag.update_unstable_expectation_id(unstable_to_stable);
10121043

1013-
let stable_id = diag
1014-
.level
1015-
.get_expectation_id()
1016-
.expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`");
1017-
inner.fulfilled_expectations.insert(stable_id);
1018-
1019-
(*TRACK_DIAGNOSTICS)(&diag);
1044+
// Here the diagnostic is given back to `emit_diagnostic` where it was first
1045+
// intercepted. Now it should be processed as usual, since the unstable expectation
1046+
// id is now stable.
1047+
inner.emit_diagnostic(&mut diag);
10201048
}
10211049
}
10221050

@@ -1066,6 +1094,15 @@ impl HandlerInner {
10661094

10671095
// FIXME(eddyb) this should ideally take `diagnostic` by value.
10681096
fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> {
1097+
// The `LintExpectationId` can be stable or unstable depending on when it was created.
1098+
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
1099+
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
1100+
// a stable one by the `LintLevelsBuilder`.
1101+
if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() {
1102+
self.unstable_expect_diagnostics.push(diagnostic.clone());
1103+
return None;
1104+
}
1105+
10691106
if diagnostic.level == Level::DelayedBug {
10701107
// FIXME(eddyb) this should check for `has_errors` and stop pushing
10711108
// once *any* errors were emitted (and truncate `delayed_span_bugs`
@@ -1082,7 +1119,12 @@ impl HandlerInner {
10821119
self.future_breakage_diagnostics.push(diagnostic.clone());
10831120
}
10841121

1085-
if diagnostic.level == Warning
1122+
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
1123+
self.suppressed_expected_diag = true;
1124+
self.fulfilled_expectations.insert(expectation_id);
1125+
}
1126+
1127+
if matches!(diagnostic.level, Warning(_))
10861128
&& !self.flags.can_emit_warnings
10871129
&& !diagnostic.is_force_warn()
10881130
{
@@ -1092,22 +1134,9 @@ impl HandlerInner {
10921134
return None;
10931135
}
10941136

1095-
// The `LintExpectationId` can be stable or unstable depending on when it was created.
1096-
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
1097-
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
1098-
// a stable one by the `LintLevelsBuilder`.
1099-
if let Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level {
1100-
self.unstable_expect_diagnostics.push(diagnostic.clone());
1101-
return None;
1102-
}
1103-
11041137
(*TRACK_DIAGNOSTICS)(diagnostic);
11051138

1106-
if let Level::Expect(expectation_id) = diagnostic.level {
1107-
self.suppressed_expected_diag = true;
1108-
self.fulfilled_expectations.insert(expectation_id);
1109-
return None;
1110-
} else if diagnostic.level == Allow {
1139+
if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
11111140
return None;
11121141
}
11131142

@@ -1144,7 +1173,7 @@ impl HandlerInner {
11441173
self.emitter.emit_diagnostic(&diagnostic);
11451174
if diagnostic.is_error() {
11461175
self.deduplicated_err_count += 1;
1147-
} else if diagnostic.level == Warning {
1176+
} else if let Warning(_) = diagnostic.level {
11481177
self.deduplicated_warn_count += 1;
11491178
}
11501179
}
@@ -1197,7 +1226,7 @@ impl HandlerInner {
11971226
match (errors.len(), warnings.len()) {
11981227
(0, 0) => return,
11991228
(0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(
1200-
Level::Warning,
1229+
Level::Warning(None),
12011230
DiagnosticMessage::Str(warnings),
12021231
)),
12031232
(_, 0) => {
@@ -1430,7 +1459,10 @@ pub enum Level {
14301459
/// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
14311460
lint: bool,
14321461
},
1433-
Warning,
1462+
/// This [`LintExpectationId`] is used for expected lint diagnostics, which should
1463+
/// also emit a warning due to the `force-warn` flag. In all other cases this should
1464+
/// be `None`.
1465+
Warning(Option<LintExpectationId>),
14341466
Note,
14351467
/// A note that is only emitted once.
14361468
OnceNote,
@@ -1453,7 +1485,7 @@ impl Level {
14531485
Bug | DelayedBug | Fatal | Error { .. } => {
14541486
spec.set_fg(Some(Color::Red)).set_intense(true);
14551487
}
1456-
Warning => {
1488+
Warning(_) => {
14571489
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
14581490
}
14591491
Note | OnceNote => {
@@ -1472,7 +1504,7 @@ impl Level {
14721504
match self {
14731505
Bug | DelayedBug => "error: internal compiler error",
14741506
Fatal | Error { .. } => "error",
1475-
Warning => "warning",
1507+
Warning(_) => "warning",
14761508
Note | OnceNote => "note",
14771509
Help => "help",
14781510
FailureNote => "failure-note",
@@ -1487,7 +1519,7 @@ impl Level {
14871519

14881520
pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
14891521
match self {
1490-
Level::Expect(id) => Some(*id),
1522+
Level::Expect(id) | Level::Warning(Some(id)) => Some(*id),
14911523
_ => None,
14921524
}
14931525
}

‎compiler/rustc_expand/src/proc_macro_server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ impl ToInternal<rustc_errors::Level> for Level {
267267
fn to_internal(self) -> rustc_errors::Level {
268268
match self {
269269
Level::Error => rustc_errors::Level::Error { lint: false },
270-
Level::Warning => rustc_errors::Level::Warning,
270+
Level::Warning => rustc_errors::Level::Warning(None),
271271
Level::Note => rustc_errors::Level::Note,
272272
Level::Help => rustc_errors::Level::Help,
273273
_ => unreachable!("unknown proc_macro::Level variant: {:?}", self),

‎compiler/rustc_lint/src/context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl LintStore {
324324
registered_tools: &RegisteredTools,
325325
) {
326326
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
327-
if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn {
327+
if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
328328
struct_span_err!(
329329
sess,
330330
DUMMY_SP,
@@ -375,7 +375,7 @@ impl LintStore {
375375
match level {
376376
Level::Allow => "-A",
377377
Level::Warn => "-W",
378-
Level::ForceWarn => "--force-warn",
378+
Level::ForceWarn(_) => "--force-warn",
379379
Level::Deny => "-D",
380380
Level::Forbid => "-F",
381381
Level::Expect(_) => {

‎compiler/rustc_lint/src/expect.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
1919
let lint_expectations = &tcx.lint_levels(()).lint_expectations;
2020

2121
for (id, expectation) in lint_expectations {
22-
if !fulfilled_expectations.contains(id)
23-
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
24-
{
25-
// This check will always be true, since `lint_expectations` only
26-
// holds stable ids
27-
if let LintExpectationId::Stable { hir_id, .. } = id {
22+
// This check will always be true, since `lint_expectations` only
23+
// holds stable ids
24+
if let LintExpectationId::Stable { hir_id, .. } = id {
25+
if !fulfilled_expectations.contains(&id)
26+
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
27+
{
2828
emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation);
29-
} else {
30-
unreachable!("at this stage all `LintExpectationId`s are stable");
3129
}
30+
} else {
31+
unreachable!("at this stage all `LintExpectationId`s are stable");
3232
}
3333
}
3434
}

‎compiler/rustc_lint/src/levels.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ impl<'s> LintLevelsBuilder<'s> {
117117
};
118118
for id in ids {
119119
// ForceWarn and Forbid cannot be overridden
120-
if let Some((Level::ForceWarn | Level::Forbid, _)) = self.current_specs().get(&id) {
120+
if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
121+
self.current_specs().get(&id)
122+
{
121123
continue;
122124
}
123125

@@ -226,11 +228,18 @@ impl<'s> LintLevelsBuilder<'s> {
226228
return;
227229
}
228230

229-
if let Level::ForceWarn = old_level {
230-
self.current_specs_mut().insert(id, (old_level, old_src));
231-
} else {
232-
self.current_specs_mut().insert(id, (level, src));
233-
}
231+
match (old_level, level) {
232+
// If the new level is an expectation store it in `ForceWarn`
233+
(Level::ForceWarn(_), Level::Expect(expectation_id)) => self
234+
.current_specs_mut()
235+
.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)),
236+
// Keep `ForceWarn` level but drop the expectation
237+
(Level::ForceWarn(_), _) => {
238+
self.current_specs_mut().insert(id, (Level::ForceWarn(None), old_src))
239+
}
240+
// Set the lint level as normal
241+
_ => self.current_specs_mut().insert(id, (level, src)),
242+
};
234243
}
235244

236245
/// Pushes a list of AST lint attributes onto this context.
@@ -269,6 +278,7 @@ impl<'s> LintLevelsBuilder<'s> {
269278

270279
let level = match Level::from_attr(attr) {
271280
None => continue,
281+
// This is the only lint level with a `LintExpectationId` that can be created from an attribute
272282
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
273283
let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index);
274284

‎compiler/rustc_lint_defs/src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,19 @@ pub enum Level {
162162
///
163163
/// See RFC 2383.
164164
///
165-
/// The `LintExpectationId` is used to later link a lint emission to the actual
165+
/// The [`LintExpectationId`] is used to later link a lint emission to the actual
166166
/// expectation. It can be ignored in most cases.
167167
Expect(LintExpectationId),
168168
/// The `warn` level will produce a warning if the lint was violated, however the
169169
/// compiler will continue with its execution.
170170
Warn,
171-
ForceWarn,
171+
/// This lint level is a special case of [`Warn`], that can't be overridden. This is used
172+
/// to ensure that a lint can't be suppressed. This lint level can currently only be set
173+
/// via the console and is therefore session specific.
174+
///
175+
/// The [`LintExpectationId`] is intended to fulfill expectations marked via the
176+
/// `#[expect]` attribute, that will still be suppressed due to the level.
177+
ForceWarn(Option<LintExpectationId>),
172178
/// The `deny` level will produce an error and stop further execution after the lint
173179
/// pass is complete.
174180
Deny,
@@ -184,7 +190,7 @@ impl Level {
184190
Level::Allow => "allow",
185191
Level::Expect(_) => "expect",
186192
Level::Warn => "warn",
187-
Level::ForceWarn => "force-warn",
193+
Level::ForceWarn(_) => "force-warn",
188194
Level::Deny => "deny",
189195
Level::Forbid => "forbid",
190196
}
@@ -219,7 +225,7 @@ impl Level {
219225

220226
pub fn is_error(self) -> bool {
221227
match self {
222-
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn => false,
228+
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
223229
Level::Deny | Level::Forbid => true,
224230
}
225231
}

‎compiler/rustc_middle/src/lint.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl LintLevelSets {
115115

116116
// Ensure that we never exceed the `--cap-lints` argument
117117
// unless the source is a --force-warn
118-
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
118+
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
119119
level
120120
} else {
121121
cmp::min(level, self.lint_cap)
@@ -266,7 +266,7 @@ pub fn explain_lint_level_source(
266266
Level::Deny => "-D",
267267
Level::Forbid => "-F",
268268
Level::Allow => "-A",
269-
Level::ForceWarn => "--force-warn",
269+
Level::ForceWarn(_) => "--force-warn",
270270
Level::Expect(_) => {
271271
unreachable!("the expect level does not have a commandline flag")
272272
}
@@ -352,8 +352,14 @@ pub fn struct_lint_level<'s, 'd>(
352352
// create a `DiagnosticBuilder` and continue as we would for warnings.
353353
sess.struct_expect("", expect_id)
354354
}
355-
(Level::Warn | Level::ForceWarn, Some(span)) => sess.struct_span_warn(span, ""),
356-
(Level::Warn | Level::ForceWarn, None) => sess.struct_warn(""),
355+
(Level::ForceWarn(Some(expect_id)), Some(span)) => {
356+
sess.struct_span_warn_with_expectation(span, "", expect_id)
357+
}
358+
(Level::ForceWarn(Some(expect_id)), None) => {
359+
sess.struct_warn_with_expectation("", expect_id)
360+
}
361+
(Level::Warn | Level::ForceWarn(None), Some(span)) => sess.struct_span_warn(span, ""),
362+
(Level::Warn | Level::ForceWarn(None), None) => sess.struct_warn(""),
357363
(Level::Deny | Level::Forbid, Some(span)) => {
358364
let mut builder = sess.diagnostic().struct_err_lint("");
359365
builder.set_span(span);
@@ -398,7 +404,7 @@ pub fn struct_lint_level<'s, 'd>(
398404
explain_lint_level_source(lint, level, src, &mut err);
399405

400406
let name = lint.name_lower();
401-
let is_force_warn = matches!(level, Level::ForceWarn);
407+
let is_force_warn = matches!(level, Level::ForceWarn(_));
402408
err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
403409

404410
if let Some(future_incompatible) = future_incompatible {

‎compiler/rustc_session/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1432,7 +1432,7 @@ pub fn get_cmd_lint_options(
14321432
let mut lint_opts_with_position = vec![];
14331433
let mut describe_lints = false;
14341434

1435-
for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
1435+
for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
14361436
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
14371437
if lint_name == "help" {
14381438
describe_lints = true;

‎compiler/rustc_session/src/session.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ impl Session {
286286
) -> DiagnosticBuilder<'_, ()> {
287287
self.diagnostic().struct_span_warn(sp, msg)
288288
}
289+
pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>(
290+
&self,
291+
sp: S,
292+
msg: impl Into<DiagnosticMessage>,
293+
id: lint::LintExpectationId,
294+
) -> DiagnosticBuilder<'_, ()> {
295+
self.diagnostic().struct_span_warn_with_expectation(sp, msg, id)
296+
}
289297
pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
290298
&self,
291299
sp: S,
@@ -297,6 +305,13 @@ impl Session {
297305
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
298306
self.diagnostic().struct_warn(msg)
299307
}
308+
pub fn struct_warn_with_expectation(
309+
&self,
310+
msg: impl Into<DiagnosticMessage>,
311+
id: lint::LintExpectationId,
312+
) -> DiagnosticBuilder<'_, ()> {
313+
self.diagnostic().struct_warn_with_expectation(msg, id)
314+
}
300315
pub fn struct_span_allow<S: Into<MultiSpan>>(
301316
&self,
302317
sp: S,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// compile-flags: --force-warn while_true
2+
// compile-flags: --force-warn unused_variables
3+
// compile-flags: --force-warn unused_mut
4+
// check-pass
5+
6+
#![feature(lint_reasons)]
7+
8+
fn expect_early_pass_lint() {
9+
#[expect(while_true)]
10+
while true {
11+
//~^ WARNING denote infinite loops with `loop { ... }` [while_true]
12+
//~| NOTE requested on the command line with `--force-warn while-true`
13+
//~| HELP use `loop`
14+
println!("I never stop")
15+
}
16+
}
17+
18+
#[expect(unused_variables, reason="<this should fail and display this reason>")]
19+
fn check_specific_lint() {
20+
let x = 2;
21+
//~^ WARNING unused variable: `x` [unused_variables]
22+
//~| NOTE requested on the command line with `--force-warn unused-variables`
23+
//~| HELP if this is intentional, prefix it with an underscore
24+
}
25+
26+
#[expect(unused)]
27+
fn check_multiple_lints_with_lint_group() {
28+
let fox_name = "Sir Nibbles";
29+
//~^ WARNING unused variable: `fox_name` [unused_variables]
30+
//~| HELP if this is intentional, prefix it with an underscore
31+
32+
let mut what_does_the_fox_say = "*ding* *deng* *dung*";
33+
//~^ WARNING variable does not need to be mutable [unused_mut]
34+
//~| NOTE requested on the command line with `--force-warn unused-mut`
35+
//~| HELP remove this `mut`
36+
37+
println!("The fox says: {what_does_the_fox_say}");
38+
}
39+
40+
#[allow(unused_variables)]
41+
fn check_expect_overrides_allow_lint_level() {
42+
#[expect(unused_variables)]
43+
let this_should_fulfill_the_expectation = "The `#[allow]` has no power here";
44+
//~^ WARNING unused variable: `this_should_fulfill_the_expectation` [unused_variables]
45+
//~| HELP if this is intentional, prefix it with an underscore
46+
}
47+
48+
fn main() {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
warning: denote infinite loops with `loop { ... }`
2+
--> $DIR/force_warn_expected_lints_fulfilled.rs:10:5
3+
|
4+
LL | while true {
5+
| ^^^^^^^^^^ help: use `loop`
6+
|
7+
= note: requested on the command line with `--force-warn while-true`
8+
9+
warning: unused variable: `x`
10+
--> $DIR/force_warn_expected_lints_fulfilled.rs:20:9
11+
|
12+
LL | let x = 2;
13+
| ^ help: if this is intentional, prefix it with an underscore: `_x`
14+
|
15+
= note: requested on the command line with `--force-warn unused-variables`
16+
17+
warning: unused variable: `fox_name`
18+
--> $DIR/force_warn_expected_lints_fulfilled.rs:28:9
19+
|
20+
LL | let fox_name = "Sir Nibbles";
21+
| ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_fox_name`
22+
23+
warning: unused variable: `this_should_fulfill_the_expectation`
24+
--> $DIR/force_warn_expected_lints_fulfilled.rs:43:9
25+
|
26+
LL | let this_should_fulfill_the_expectation = "The `#[allow]` has no power here";
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_this_should_fulfill_the_expectation`
28+
29+
warning: variable does not need to be mutable
30+
--> $DIR/force_warn_expected_lints_fulfilled.rs:32:9
31+
|
32+
LL | let mut what_does_the_fox_say = "*ding* *deng* *dung*";
33+
| ----^^^^^^^^^^^^^^^^^^^^^
34+
| |
35+
| help: remove this `mut`
36+
|
37+
= note: requested on the command line with `--force-warn unused-mut`
38+
39+
warning: 5 warnings emitted
40+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// compile-flags: --force-warn while_true
2+
// compile-flags: --force-warn unused_variables
3+
// compile-flags: --force-warn unused_mut
4+
// check-pass
5+
6+
#![feature(lint_reasons)]
7+
8+
fn expect_early_pass_lint(terminate: bool) {
9+
#[expect(while_true)]
10+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
11+
//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default
12+
while !terminate {
13+
println!("Do you know what a spin lock is?")
14+
}
15+
}
16+
17+
#[expect(unused_variables, reason="<this should fail and display this reason>")]
18+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
19+
//~| NOTE <this should fail and display this reason>
20+
fn check_specific_lint() {
21+
let _x = 2;
22+
}
23+
24+
#[expect(unused)]
25+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
26+
fn check_multiple_lints_with_lint_group() {
27+
let fox_name = "Sir Nibbles";
28+
29+
let what_does_the_fox_say = "*ding* *deng* *dung*";
30+
31+
println!("The fox says: {what_does_the_fox_say}");
32+
println!("~ {fox_name}")
33+
}
34+
35+
36+
#[expect(unused)]
37+
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
38+
fn check_overridden_expectation_lint_level() {
39+
#[allow(unused_variables)]
40+
let this_should_not_fulfill_the_expectation = "maybe";
41+
//~^ WARNING unused variable: `this_should_not_fulfill_the_expectation` [unused_variables]
42+
//~| NOTE requested on the command line with `--force-warn unused-variables`
43+
//~| HELP if this is intentional, prefix it with an underscore
44+
}
45+
46+
fn main() {
47+
check_multiple_lints_with_lint_group();
48+
check_overridden_expectation_lint_level();
49+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
warning: unused variable: `this_should_not_fulfill_the_expectation`
2+
--> $DIR/force_warn_expected_lints_unfulfilled.rs:40:9
3+
|
4+
LL | let this_should_not_fulfill_the_expectation = "maybe";
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_this_should_not_fulfill_the_expectation`
6+
|
7+
= note: requested on the command line with `--force-warn unused-variables`
8+
9+
warning: this lint expectation is unfulfilled
10+
--> $DIR/force_warn_expected_lints_unfulfilled.rs:9:14
11+
|
12+
LL | #[expect(while_true)]
13+
| ^^^^^^^^^^
14+
|
15+
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
16+
17+
warning: this lint expectation is unfulfilled
18+
--> $DIR/force_warn_expected_lints_unfulfilled.rs:17:10
19+
|
20+
LL | #[expect(unused_variables, reason="<this should fail and display this reason>")]
21+
| ^^^^^^^^^^^^^^^^
22+
|
23+
= note: <this should fail and display this reason>
24+
25+
warning: this lint expectation is unfulfilled
26+
--> $DIR/force_warn_expected_lints_unfulfilled.rs:24:10
27+
|
28+
LL | #[expect(unused)]
29+
| ^^^^^^
30+
31+
warning: this lint expectation is unfulfilled
32+
--> $DIR/force_warn_expected_lints_unfulfilled.rs:36:10
33+
|
34+
LL | #[expect(unused)]
35+
| ^^^^^^
36+
37+
warning: 5 warnings emitted
38+

‎src/tools/rustfmt/src/parse/session.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ mod tests {
433433
Some(ignore_list),
434434
);
435435
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
436-
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
436+
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
437437
emitter.emit_diagnostic(&non_fatal_diagnostic);
438438
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
439439
assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
@@ -457,7 +457,7 @@ mod tests {
457457
None,
458458
);
459459
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
460-
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
460+
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
461461
emitter.emit_diagnostic(&non_fatal_diagnostic);
462462
assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
463463
assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
@@ -494,8 +494,8 @@ mod tests {
494494
);
495495
let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
496496
let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
497-
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
498-
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
497+
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span));
498+
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span));
499499
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
500500
emitter.emit_diagnostic(&bar_diagnostic);
501501
emitter.emit_diagnostic(&foo_diagnostic);

0 commit comments

Comments
 (0)
Please sign in to comment.