Allow errors to be emitted as fatal during attribute parsing
This commit is contained in:
@@ -5,7 +5,7 @@ use std::sync::LazyLock;
|
||||
|
||||
use private::Sealed;
|
||||
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_errors::{Diag, Diagnostic, Level};
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
|
||||
@@ -263,11 +263,7 @@ impl Stage for Early {
|
||||
sess: &'sess Session,
|
||||
diag: impl for<'x> Diagnostic<'x>,
|
||||
) -> ErrorGuaranteed {
|
||||
if self.emit_errors.should_emit() {
|
||||
sess.dcx().emit_err(diag)
|
||||
} else {
|
||||
sess.dcx().create_err(diag).delay_as_bug()
|
||||
}
|
||||
self.should_emit().emit_err_or_delay(sess.dcx().create_err(diag))
|
||||
}
|
||||
|
||||
fn should_emit(&self) -> ShouldEmit {
|
||||
@@ -333,7 +329,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
|
||||
/// must be delayed until after HIR is built. This method will take care of the details of
|
||||
/// that.
|
||||
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
|
||||
if !self.stage.should_emit().should_emit() {
|
||||
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
return;
|
||||
}
|
||||
let id = self.target_id;
|
||||
@@ -651,8 +647,13 @@ pub enum OmitDoc {
|
||||
Skip,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ShouldEmit {
|
||||
/// The operations will emit errors, and lints, and errors are fatal.
|
||||
///
|
||||
/// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`.
|
||||
/// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible.
|
||||
EarlyFatal,
|
||||
/// The operation will emit errors and lints.
|
||||
/// This is usually what you need.
|
||||
ErrorsAndLints,
|
||||
@@ -662,10 +663,27 @@ pub enum ShouldEmit {
|
||||
}
|
||||
|
||||
impl ShouldEmit {
|
||||
pub fn should_emit(&self) -> bool {
|
||||
pub(crate) fn emit_err_or_delay(&self, diag: Diag<'_>) -> ErrorGuaranteed {
|
||||
match self {
|
||||
ShouldEmit::ErrorsAndLints => true,
|
||||
ShouldEmit::Nothing => false,
|
||||
ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(),
|
||||
ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(),
|
||||
ShouldEmit::ErrorsAndLints => diag.emit(),
|
||||
ShouldEmit::Nothing => diag.delay_as_bug(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_emit_err(&self, diag: Diag<'_>) {
|
||||
match self {
|
||||
ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => {
|
||||
diag.emit();
|
||||
}
|
||||
ShouldEmit::EarlyFatal => {
|
||||
diag.upgrade_to_fatal().emit();
|
||||
}
|
||||
ShouldEmit::ErrorsAndLints => {
|
||||
diag.emit();
|
||||
}
|
||||
ShouldEmit::Nothing => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
|
||||
(accept.accept_fn)(&mut cx, args);
|
||||
|
||||
if self.stage.should_emit().should_emit() {
|
||||
if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
|
||||
self.check_target(
|
||||
path.get_attribute_path(),
|
||||
attr.span,
|
||||
|
||||
@@ -328,15 +328,16 @@ fn expr_to_lit(
|
||||
match res {
|
||||
Ok(lit) => {
|
||||
if token_lit.suffix.is_some() {
|
||||
psess
|
||||
.dcx()
|
||||
.create_err(SuffixedLiteralInAttribute { span: lit.span })
|
||||
.emit_unless_delay(!should_emit.should_emit());
|
||||
should_emit.emit_err_or_delay(
|
||||
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
None
|
||||
} else {
|
||||
if should_emit.should_emit() && !lit.kind.is_unsuffixed() {
|
||||
if !lit.kind.is_unsuffixed() {
|
||||
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
||||
psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
|
||||
should_emit.maybe_emit_err(
|
||||
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
}
|
||||
|
||||
Some(lit)
|
||||
@@ -366,7 +367,7 @@ fn expr_to_lit(
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
|
||||
err.emit_unless_delay(!should_emit.should_emit());
|
||||
should_emit.emit_err_or_delay(err);
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -397,9 +398,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
||||
}
|
||||
};
|
||||
|
||||
if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() {
|
||||
if !lit.kind.is_unsuffixed() {
|
||||
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
||||
self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
|
||||
self.should_emit.maybe_emit_err(
|
||||
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(lit)
|
||||
@@ -539,7 +542,7 @@ impl<'a> MetaItemListParser<'a> {
|
||||
) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
e.emit_unless_delay(!should_emit.should_emit());
|
||||
should_emit.emit_err_or_delay(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,6 +577,29 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
|
||||
self.level = Level::DelayedBug;
|
||||
}
|
||||
|
||||
/// Make emitting this diagnostic fatal
|
||||
///
|
||||
/// Changes the level of this diagnostic to Fatal, and importantly also changes the emission guarantee.
|
||||
/// This is sound for errors that would otherwise be printed, but now simply exit the process instead.
|
||||
/// This function still gives an emission guarantee, the guarantee is now just that it exits fatally.
|
||||
/// For delayed bugs this is different, since those are buffered. If we upgrade one to fatal, another
|
||||
/// might now be ignored.
|
||||
#[rustc_lint_diagnostics]
|
||||
#[track_caller]
|
||||
pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> {
|
||||
assert!(
|
||||
matches!(self.level, Level::Error),
|
||||
"upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error",
|
||||
self.level
|
||||
);
|
||||
self.level = Level::Fatal;
|
||||
|
||||
// Take is okay since we immediately rewrap it in another diagnostic.
|
||||
// i.e. we do emit it despite defusing the original diagnostic's drop bomb.
|
||||
let diag = self.diag.take();
|
||||
Diag { dcx: self.dcx, diag, _marker: PhantomData }
|
||||
}
|
||||
|
||||
with_fn! { with_span_label,
|
||||
/// Appends a labeled span to the diagnostic.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user