Allow Early stage to emit errors

This commit is contained in:
Jonathan Brouwer
2025-07-07 09:06:44 +02:00
parent a14baf1e56
commit 38dd6f5206
5 changed files with 93 additions and 18 deletions

View File

@@ -1,6 +1,5 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
@@ -202,7 +201,11 @@ pub trait Stage: Sized + 'static + Sealed {
fn parsers() -> &'static group_type!(Self);
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed;
fn emit_err<'sess>(
&self,
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed;
}
// allow because it's a sealed trait
@@ -214,8 +217,16 @@ impl Stage for Early {
fn parsers() -> &'static group_type!(Self) {
&early::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
sess.dcx().create_err(diag).delay_as_bug()
fn emit_err<'sess>(
&self,
sess: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed {
if self.emit_errors {
sess.dcx().emit_err(diag)
} else {
sess.dcx().create_err(diag).delay_as_bug()
}
}
}
@@ -228,20 +239,29 @@ impl Stage for Late {
fn parsers() -> &'static group_type!(Self) {
&late::ATTRIBUTE_PARSERS
}
fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
fn emit_err<'sess>(
&self,
tcx: &'sess Session,
diag: impl for<'x> Diagnostic<'x>,
) -> ErrorGuaranteed {
tcx.dcx().emit_err(diag)
}
}
/// used when parsing attributes for miscellaneous things *before* ast lowering
pub struct Early;
pub struct Early {
/// Whether to emit errors or delay them as a bug
/// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed
/// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted
pub emit_errors: bool,
}
/// used when parsing attributes during ast lowering
pub struct Late;
/// Context given to every attribute parser when accepting
///
/// Gives [`AttributeParser`]s enough information to create errors, for example.
pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
pub struct AcceptContext<'f, 'sess, S: Stage> {
pub(crate) shared: SharedContext<'f, 'sess, S>,
/// The span of the attribute currently being parsed
pub(crate) attr_span: Span,
@@ -257,7 +277,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
S::emit_err(&self.sess, diag)
self.stage.emit_err(&self.sess, diag)
}
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
@@ -472,7 +492,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
///
/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
/// errors, for example.
pub(crate) struct SharedContext<'p, 'sess, S: Stage> {
pub struct SharedContext<'p, 'sess, S: Stage> {
/// The parse context, gives access to the session and the
/// diagnostics context.
pub(crate) cx: &'p mut AttributeParser<'sess, S>,
@@ -540,7 +560,7 @@ pub struct AttributeParser<'sess, S: Stage = Late> {
pub(crate) tools: Vec<Symbol>,
features: Option<&'sess Features>,
sess: &'sess Session,
stage: PhantomData<S>,
stage: S,
/// *Only* parse attributes with this symbol.
///
@@ -569,13 +589,14 @@ impl<'sess> AttributeParser<'sess, Early> {
sym: Symbol,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
) -> Option<Attribute> {
let mut p = Self {
features: None,
features,
tools: Vec::new(),
parse_only: Some(sym),
sess,
stage: PhantomData,
stage: Early { emit_errors: false },
};
let mut parsed = p.parse_attribute_list(
attrs,
@@ -591,11 +612,55 @@ impl<'sess> AttributeParser<'sess, Early> {
parsed.pop()
}
pub fn parse_single<T>(
sess: &'sess Session,
attr: &ast::Attribute,
target_span: Span,
target_node_id: NodeId,
features: Option<&'sess Features>,
emit_errors: bool,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
template: &AttributeTemplate,
) -> T {
let mut parser = Self {
features,
tools: Vec::new(),
parse_only: None,
sess,
stage: Early { emit_errors },
};
let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
panic!("parse_single called on a doc attr")
};
let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
let path = meta_parser.path();
let args = meta_parser.args();
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
target_span,
target_id: target_node_id,
emit_lint: &mut |_lint| {
panic!("can't emit lints here for now (nothing uses this atm)");
},
},
attr_span: attr.span,
template,
attr_path: path.get_attribute_path(),
};
parse_fn(&mut cx, args)
}
}
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
pub fn new(
sess: &'sess Session,
features: &'sess Features,
tools: Vec<Symbol>,
stage: S,
) -> Self {
Self { features: Some(features), tools, parse_only: None, sess, stage }
}
pub(crate) fn sess(&self) -> &'sess Session {
@@ -606,6 +671,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
self.features.expect("features not available at this point in the compiler")
}
pub(crate) fn features_option(&self) -> Option<&'sess Features> {
self.features
}
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
self.sess().dcx()
}