Skip to content

Commit 097f40c

Browse files
committed
Allow errors to be emitted as fatal during attribute parsing
1 parent 8df154b commit 097f40c

File tree

4 files changed

+66
-22
lines changed

4 files changed

+66
-22
lines changed

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::sync::LazyLock;
55

66
use private::Sealed;
77
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
8-
use rustc_errors::Diagnostic;
8+
use rustc_errors::{Diag, Diagnostic, Level};
99
use rustc_feature::AttributeTemplate;
1010
use rustc_hir::attrs::AttributeKind;
1111
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
@@ -261,11 +261,7 @@ impl Stage for Early {
261261
sess: &'sess Session,
262262
diag: impl for<'x> Diagnostic<'x>,
263263
) -> ErrorGuaranteed {
264-
if self.emit_errors.should_emit() {
265-
sess.dcx().emit_err(diag)
266-
} else {
267-
sess.dcx().create_err(diag).delay_as_bug()
268-
}
264+
self.should_emit().emit_err_or_delay(sess.dcx().create_err(diag))
269265
}
270266

271267
fn should_emit(&self) -> ShouldEmit {
@@ -331,7 +327,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
331327
/// must be delayed until after HIR is built. This method will take care of the details of
332328
/// that.
333329
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
334-
if !self.stage.should_emit().should_emit() {
330+
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
335331
return;
336332
}
337333
let id = self.target_id;
@@ -649,8 +645,13 @@ pub enum OmitDoc {
649645
Skip,
650646
}
651647

652-
#[derive(Copy, Clone)]
648+
#[derive(Copy, Clone, Debug)]
653649
pub enum ShouldEmit {
650+
/// The operations will emit errors, and lints, and errors are fatal.
651+
///
652+
/// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`.
653+
/// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible.
654+
EarlyFatal,
654655
/// The operation will emit errors and lints.
655656
/// This is usually what you need.
656657
ErrorsAndLints,
@@ -660,10 +661,27 @@ pub enum ShouldEmit {
660661
}
661662

662663
impl ShouldEmit {
663-
pub fn should_emit(&self) -> bool {
664+
pub(crate) fn emit_err_or_delay(&self, diag: Diag<'_>) -> ErrorGuaranteed {
665+
match self {
666+
ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(),
667+
ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(),
668+
ShouldEmit::ErrorsAndLints => diag.emit(),
669+
ShouldEmit::Nothing => diag.delay_as_bug(),
670+
}
671+
}
672+
673+
pub(crate) fn maybe_emit_err(&self, diag: Diag<'_>) {
664674
match self {
665-
ShouldEmit::ErrorsAndLints => true,
666-
ShouldEmit::Nothing => false,
675+
ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => {
676+
diag.emit();
677+
}
678+
ShouldEmit::EarlyFatal => {
679+
diag.upgrade_to_fatal().emit();
680+
}
681+
ShouldEmit::ErrorsAndLints => {
682+
diag.emit();
683+
}
684+
ShouldEmit::Nothing => {}
667685
}
668686
}
669687
}

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
252252

253253
(accept.accept_fn)(&mut cx, args);
254254

255-
if self.stage.should_emit().should_emit() {
255+
if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
256256
self.check_target(
257257
path.get_attribute_path(),
258258
attr.span,

compiler/rustc_attr_parsing/src/parser.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,16 @@ fn expr_to_lit(
328328
match res {
329329
Ok(lit) => {
330330
if token_lit.suffix.is_some() {
331-
psess
332-
.dcx()
333-
.create_err(SuffixedLiteralInAttribute { span: lit.span })
334-
.emit_unless_delay(!should_emit.should_emit());
331+
should_emit.emit_err_or_delay(
332+
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
333+
);
335334
None
336335
} else {
337-
if should_emit.should_emit() && !lit.kind.is_unsuffixed() {
336+
if !lit.kind.is_unsuffixed() {
338337
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
339-
psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
338+
should_emit.maybe_emit_err(
339+
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
340+
);
340341
}
341342

342343
Some(lit)
@@ -366,7 +367,7 @@ fn expr_to_lit(
366367
err.downgrade_to_delayed_bug();
367368
}
368369

369-
err.emit_unless_delay(!should_emit.should_emit());
370+
should_emit.emit_err_or_delay(err);
370371
None
371372
}
372373
}
@@ -397,9 +398,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
397398
}
398399
};
399400

400-
if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() {
401+
if !lit.kind.is_unsuffixed() {
401402
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
402-
self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
403+
self.should_emit.maybe_emit_err(
404+
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
405+
);
403406
}
404407

405408
Ok(lit)
@@ -539,7 +542,7 @@ impl<'a> MetaItemListParser<'a> {
539542
) {
540543
Ok(s) => Some(s),
541544
Err(e) => {
542-
e.emit_unless_delay(!should_emit.should_emit());
545+
should_emit.emit_err_or_delay(e);
543546
None
544547
}
545548
}

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,29 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
577577
self.level = Level::DelayedBug;
578578
}
579579

580+
/// Make emitting this diagnostic fatal
581+
///
582+
/// Changes the level of this diagnostic to Fatal, and importantly also changes the emission guarantee.
583+
/// This is sound for errors that would otherwise be printed, but now simply exit the process instead.
584+
/// This function still gives an emission guarantee, the guarantee is now just that it exits fatally.
585+
/// For delayed bugs this is different, since those are buffered. If we upgrade one to fatal, another
586+
/// might now be ignored.
587+
#[rustc_lint_diagnostics]
588+
#[track_caller]
589+
pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> {
590+
assert!(
591+
matches!(self.level, Level::Error),
592+
"upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error",
593+
self.level
594+
);
595+
self.level = Level::Fatal;
596+
597+
// Take is okay since we immediately rewrap it in another diagnostic.
598+
// i.e. we do emit it despite defusing the original diagnostic's drop bomb.
599+
let diag = self.diag.take();
600+
Diag { dcx: self.dcx, diag, _marker: PhantomData }
601+
}
602+
580603
with_fn! { with_span_label,
581604
/// Appends a labeled span to the diagnostic.
582605
///

0 commit comments

Comments
 (0)