Skip to content

Commit 95be954

Browse files
authored
Rollup merge of rust-lang#97757 - xFrednet:rfc-2383-expect-with-force-warn, r=wesleywiser,flip1995
Support lint expectations for `--force-warn` lints (RFC 2383) Rustc has a `--force-warn` flag, which overrides lint level attributes and forces the diagnostics to always be warn. This means, that for lint expectations, the diagnostic can't be suppressed as usual. This also means that the expectation would not be fulfilled, even if a lint had been triggered in the expected scope. This PR now also tracks the expectation ID in the `ForceWarn` level. I've also made some minor adjustments, to possibly catch more bugs and make the whole implementation more robust. This will probably conflict with rust-lang#97718. That PR should ideally be reviewed and merged first. The conflict itself will be trivial to fix. --- r? `@wesleywiser` cc: `@flip1995` since you've helped with the initial review and also discussed this topic with me. 🙃 Follow-up of: rust-lang#87835 Issue: rust-lang#85549 Yeah, and that's it.
2 parents 1b9daa6 + 8527a3d commit 95be954

File tree

19 files changed

+317
-71
lines changed

19 files changed

+317
-71
lines changed

compiler/rustc_codegen_llvm/src/back/write.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ fn report_inline_asm(
336336
}
337337
let level = match level {
338338
llvm::DiagnosticLevel::Error => Level::Error { lint: false },
339-
llvm::DiagnosticLevel::Warning => Level::Warning,
339+
llvm::DiagnosticLevel::Warning => Level::Warning(None),
340340
llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
341341
};
342342
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);

compiler/rustc_codegen_ssa/src/back/write.rs

+1-1
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

+1-1
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

+6-4
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl Diagnostic {
209209
| Level::Error { .. }
210210
| Level::FailureNote => true,
211211

212-
Level::Warning
212+
Level::Warning(_)
213213
| Level::Note
214214
| Level::OnceNote
215215
| Level::Help
@@ -222,7 +222,9 @@ impl Diagnostic {
222222
&mut self,
223223
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
224224
) {
225-
if let Level::Expect(expectation_id) = &mut self.level {
225+
if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
226+
&mut self.level
227+
{
226228
if expectation_id.is_stable() {
227229
return;
228230
}
@@ -450,7 +452,7 @@ impl Diagnostic {
450452
/// Add a warning attached to this diagnostic.
451453
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
452454
pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
453-
self.sub(Level::Warning, msg, MultiSpan::new(), None);
455+
self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
454456
self
455457
}
456458

@@ -462,7 +464,7 @@ impl Diagnostic {
462464
sp: S,
463465
msg: impl Into<SubdiagnosticMessage>,
464466
) -> &mut Self {
465-
self.sub(Level::Warning, msg, sp.into(), None);
467+
self.sub(Level::Warning(None), msg, sp.into(), None);
466468
self
467469
}
468470

compiler/rustc_errors/src/json.rs

+1-1
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

+64-32
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,23 @@ impl Handler {
660660
result
661661
}
662662

663+
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
664+
/// The `id` is used for lint emissions which should also fulfill a lint expectation.
665+
///
666+
/// Attempting to `.emit()` the builder will only emit if either:
667+
/// * `can_emit_warnings` is `true`
668+
/// * `is_force_warn` was set in `DiagnosticId::Lint`
669+
pub fn struct_span_warn_with_expectation(
670+
&self,
671+
span: impl Into<MultiSpan>,
672+
msg: impl Into<DiagnosticMessage>,
673+
id: LintExpectationId,
674+
) -> DiagnosticBuilder<'_, ()> {
675+
let mut result = self.struct_warn_with_expectation(msg, id);
676+
result.set_span(span);
677+
result
678+
}
679+
663680
/// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
664681
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
665682
pub fn struct_span_allow(
@@ -693,7 +710,21 @@ impl Handler {
693710
/// * `is_force_warn` was set in `DiagnosticId::Lint`
694711
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
695712
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
696-
DiagnosticBuilder::new(self, Level::Warning, msg)
713+
DiagnosticBuilder::new(self, Level::Warning(None), msg)
714+
}
715+
716+
/// Construct a builder at the `Warning` level with the `msg`. The `id` is used for
717+
/// lint emissions which should also fulfill a lint expectation.
718+
///
719+
/// Attempting to `.emit()` the builder will only emit if either:
720+
/// * `can_emit_warnings` is `true`
721+
/// * `is_force_warn` was set in `DiagnosticId::Lint`
722+
pub fn struct_warn_with_expectation(
723+
&self,
724+
msg: impl Into<DiagnosticMessage>,
725+
id: LintExpectationId,
726+
) -> DiagnosticBuilder<'_, ()> {
727+
DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg)
697728
}
698729

699730
/// Construct a builder at the `Allow` level with the `msg`.
@@ -864,7 +895,7 @@ impl Handler {
864895

865896
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
866897
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
867-
self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
898+
self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span);
868899
}
869900

870901
#[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
@@ -874,7 +905,7 @@ impl Handler {
874905
msg: impl Into<DiagnosticMessage>,
875906
code: DiagnosticId,
876907
) {
877-
self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span);
908+
self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span);
878909
}
879910

880911
pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
@@ -928,7 +959,7 @@ impl Handler {
928959
}
929960

930961
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
931-
let mut db = DiagnosticBuilder::new(self, Warning, msg);
962+
let mut db = DiagnosticBuilder::new(self, Warning(None), msg);
932963
db.emit();
933964
}
934965

@@ -1033,13 +1064,10 @@ impl Handler {
10331064
for mut diag in diags.into_iter() {
10341065
diag.update_unstable_expectation_id(unstable_to_stable);
10351066

1036-
let stable_id = diag
1037-
.level
1038-
.get_expectation_id()
1039-
.expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`");
1040-
inner.fulfilled_expectations.insert(stable_id);
1041-
1042-
(*TRACK_DIAGNOSTICS)(&diag);
1067+
// Here the diagnostic is given back to `emit_diagnostic` where it was first
1068+
// intercepted. Now it should be processed as usual, since the unstable expectation
1069+
// id is now stable.
1070+
inner.emit_diagnostic(&mut diag);
10431071
}
10441072
}
10451073

@@ -1089,6 +1117,15 @@ impl HandlerInner {
10891117

10901118
// FIXME(eddyb) this should ideally take `diagnostic` by value.
10911119
fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> {
1120+
// The `LintExpectationId` can be stable or unstable depending on when it was created.
1121+
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
1122+
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
1123+
// a stable one by the `LintLevelsBuilder`.
1124+
if let Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() {
1125+
self.unstable_expect_diagnostics.push(diagnostic.clone());
1126+
return None;
1127+
}
1128+
10921129
if diagnostic.level == Level::DelayedBug {
10931130
// FIXME(eddyb) this should check for `has_errors` and stop pushing
10941131
// once *any* errors were emitted (and truncate `delayed_span_bugs`
@@ -1105,7 +1142,12 @@ impl HandlerInner {
11051142
self.future_breakage_diagnostics.push(diagnostic.clone());
11061143
}
11071144

1108-
if diagnostic.level == Warning
1145+
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
1146+
self.suppressed_expected_diag = true;
1147+
self.fulfilled_expectations.insert(expectation_id);
1148+
}
1149+
1150+
if matches!(diagnostic.level, Warning(_))
11091151
&& !self.flags.can_emit_warnings
11101152
&& !diagnostic.is_force_warn()
11111153
{
@@ -1115,22 +1157,9 @@ impl HandlerInner {
11151157
return None;
11161158
}
11171159

1118-
// The `LintExpectationId` can be stable or unstable depending on when it was created.
1119-
// Diagnostics created before the definition of `HirId`s are unstable and can not yet
1120-
// be stored. Instead, they are buffered until the `LintExpectationId` is replaced by
1121-
// a stable one by the `LintLevelsBuilder`.
1122-
if let Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level {
1123-
self.unstable_expect_diagnostics.push(diagnostic.clone());
1124-
return None;
1125-
}
1126-
11271160
(*TRACK_DIAGNOSTICS)(diagnostic);
11281161

1129-
if let Level::Expect(expectation_id) = diagnostic.level {
1130-
self.suppressed_expected_diag = true;
1131-
self.fulfilled_expectations.insert(expectation_id);
1132-
return None;
1133-
} else if diagnostic.level == Allow {
1162+
if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
11341163
return None;
11351164
}
11361165

@@ -1167,7 +1196,7 @@ impl HandlerInner {
11671196
self.emitter.emit_diagnostic(&diagnostic);
11681197
if diagnostic.is_error() {
11691198
self.deduplicated_err_count += 1;
1170-
} else if diagnostic.level == Warning {
1199+
} else if let Warning(_) = diagnostic.level {
11711200
self.deduplicated_warn_count += 1;
11721201
}
11731202
}
@@ -1220,7 +1249,7 @@ impl HandlerInner {
12201249
match (errors.len(), warnings.len()) {
12211250
(0, 0) => return,
12221251
(0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(
1223-
Level::Warning,
1252+
Level::Warning(None),
12241253
DiagnosticMessage::Str(warnings),
12251254
)),
12261255
(_, 0) => {
@@ -1453,7 +1482,10 @@ pub enum Level {
14531482
/// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
14541483
lint: bool,
14551484
},
1456-
Warning,
1485+
/// This [`LintExpectationId`] is used for expected lint diagnostics, which should
1486+
/// also emit a warning due to the `force-warn` flag. In all other cases this should
1487+
/// be `None`.
1488+
Warning(Option<LintExpectationId>),
14571489
Note,
14581490
/// A note that is only emitted once.
14591491
OnceNote,
@@ -1476,7 +1508,7 @@ impl Level {
14761508
Bug | DelayedBug | Fatal | Error { .. } => {
14771509
spec.set_fg(Some(Color::Red)).set_intense(true);
14781510
}
1479-
Warning => {
1511+
Warning(_) => {
14801512
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
14811513
}
14821514
Note | OnceNote => {
@@ -1495,7 +1527,7 @@ impl Level {
14951527
match self {
14961528
Bug | DelayedBug => "error: internal compiler error",
14971529
Fatal | Error { .. } => "error",
1498-
Warning => "warning",
1530+
Warning(_) => "warning",
14991531
Note | OnceNote => "note",
15001532
Help => "help",
15011533
FailureNote => "failure-note",
@@ -1510,7 +1542,7 @@ impl Level {
15101542

15111543
pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
15121544
match self {
1513-
Level::Expect(id) => Some(*id),
1545+
Level::Expect(id) | Level::Warning(Some(id)) => Some(*id),
15141546
_ => None,
15151547
}
15161548
}

compiler/rustc_expand/src/proc_macro_server.rs

+1-1
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

+2-2
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

+8-8
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

+16-6
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

0 commit comments

Comments
 (0)