delay expand macro bang when there has indeterminate path

This commit is contained in:
bohan 2024-03-12 10:55:17 +08:00
parent 5a6c1aa2bc
commit 8fcdf54a6b
22 changed files with 509 additions and 263 deletions

View file

@ -245,6 +245,15 @@ pub enum ExpandResult<T, U> {
Retry(U),
}
impl<T, U> ExpandResult<T, U> {
pub fn map<E, F: FnOnce(T) -> E>(self, f: F) -> ExpandResult<E, U> {
match self {
ExpandResult::Ready(t) => ExpandResult::Ready(f(t)),
ExpandResult::Retry(u) => ExpandResult::Retry(u),
}
}
}
pub trait MultiItemModifier {
/// `meta_item` is the attribute, and `item` is the item being modified.
fn expand(
@ -330,22 +339,24 @@ pub trait TTMacroExpander {
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx>;
) -> MacroExpanderResult<'cx>;
}
pub type MacroExpanderResult<'cx> = ExpandResult<Box<dyn MacResult + 'cx>, ()>;
pub type MacroExpanderFn =
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box<dyn MacResult + 'cx>;
for<'cx> fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>;
impl<F> TTMacroExpander for F
where
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> Box<dyn MacResult + 'cx>,
F: for<'cx> Fn(&'cx mut ExtCtxt<'_>, Span, TokenStream) -> MacroExpanderResult<'cx>,
{
fn expand<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx> {
) -> MacroExpanderResult<'cx> {
self(ecx, span, input)
}
}
@ -904,8 +915,11 @@ impl SyntaxExtension {
cx: &'cx mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> Box<dyn MacResult + 'cx> {
DummyResult::any(span, cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"))
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(DummyResult::any(
span,
cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"),
))
}
SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Box::new(expander)), edition)
}
@ -1008,6 +1022,11 @@ pub trait ResolverExpand {
expn_id: LocalExpnId,
path: &ast::Path,
) -> Result<bool, Indeterminate>;
fn macro_accessible(
&mut self,
expn_id: LocalExpnId,
path: &ast::Path,
) -> Result<bool, Indeterminate>;
/// Decodes the proc-macro quoted span in the specified crate, with the specified id.
/// No caching is performed.
@ -1253,6 +1272,15 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
}
}
/// `Ok` represents successfully retrieving the string literal at the correct
/// position, e.g., `println("abc")`.
type ExprToSpannedStringResult<'a> = Result<(Symbol, ast::StrStyle, Span), UnexpectedExprKind<'a>>;
/// - `Ok` is returned when the conversion to a string literal is unsuccessful,
/// but another type of expression is obtained instead.
/// - `Err` is returned when the conversion process fails.
type UnexpectedExprKind<'a> = Result<(Diag<'a>, bool /* has_suggestions */), ErrorGuaranteed>;
/// Extracts a string literal from the macro expanded version of `expr`,
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
/// The returned bool indicates whether an applicable suggestion has already been
@ -1264,17 +1292,23 @@ pub fn expr_to_spanned_string<'a>(
cx: &'a mut ExtCtxt<'_>,
expr: P<ast::Expr>,
err_msg: &'static str,
) -> Result<
(Symbol, ast::StrStyle, Span),
Result<(Diag<'a>, bool /* has_suggestions */), ErrorGuaranteed>,
> {
) -> ExpandResult<ExprToSpannedStringResult<'a>, ()> {
if !cx.force_mode
&& let ast::ExprKind::MacCall(m) = &expr.kind
&& cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err()
{
return ExpandResult::Retry(());
}
// Perform eager expansion on the expression.
// We want to be able to handle e.g., `concat!("foo", "bar")`.
let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
Err(match expr.kind {
ExpandResult::Ready(Err(match expr.kind {
ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
Ok(ast::LitKind::Str(s, style)) => return Ok((s, style, expr.span)),
Ok(ast::LitKind::Str(s, style)) => {
return ExpandResult::Ready(Ok((s, style, expr.span)));
}
Ok(ast::LitKind::ByteStr(..)) => {
let mut err = cx.dcx().struct_span_err(expr.span, err_msg);
let span = expr.span.shrink_to_lo();
@ -1295,7 +1329,7 @@ pub fn expr_to_spanned_string<'a>(
cx.dcx().span_bug(expr.span, "tried to get a string literal from `ExprKind::Dummy`")
}
_ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)),
})
}))
}
/// Extracts a string literal from the macro expanded version of `expr`,
@ -1305,13 +1339,14 @@ pub fn expr_to_string(
cx: &mut ExtCtxt<'_>,
expr: P<ast::Expr>,
err_msg: &'static str,
) -> Result<(Symbol, ast::StrStyle), ErrorGuaranteed> {
expr_to_spanned_string(cx, expr, err_msg)
.map_err(|err| match err {
) -> ExpandResult<Result<(Symbol, ast::StrStyle), ErrorGuaranteed>, ()> {
expr_to_spanned_string(cx, expr, err_msg).map(|res| {
res.map_err(|err| match err {
Ok((err, _)) => err.emit(),
Err(guar) => guar,
})
.map(|(symbol, style, _)| (symbol, style))
})
}
/// Non-fatally assert that `tts` is empty. Note that this function
@ -1343,19 +1378,22 @@ pub fn get_single_str_from_tts(
span: Span,
tts: TokenStream,
name: &str,
) -> Result<Symbol, ErrorGuaranteed> {
) -> ExpandResult<Result<Symbol, ErrorGuaranteed>, ()> {
let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof {
let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
return Err(guar);
return ExpandResult::Ready(Err(guar));
}
let ret = parse_expr(&mut p)?;
let ret = match parse_expr(&mut p) {
Ok(ret) => ret,
Err(guar) => return ExpandResult::Ready(Err(guar)),
};
let _ = p.eat(&token::Comma);
if p.token != token::Eof {
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
expr_to_string(cx, ret, "argument must be a string literal").map(|s| s.map(|(s, _)| s))
}
/// Extracts comma-separated expressions from `tts`.
@ -1363,11 +1401,20 @@ pub fn get_single_str_from_tts(
pub fn get_exprs_from_tts(
cx: &mut ExtCtxt<'_>,
tts: TokenStream,
) -> Result<Vec<P<ast::Expr>>, ErrorGuaranteed> {
) -> ExpandResult<Result<Vec<P<ast::Expr>>, ErrorGuaranteed>, ()> {
let mut p = cx.new_parser_from_tts(tts);
let mut es = Vec::new();
while p.token != token::Eof {
let expr = parse_expr(&mut p)?;
let expr = match parse_expr(&mut p) {
Ok(expr) => expr,
Err(guar) => return ExpandResult::Ready(Err(guar)),
};
if !cx.force_mode
&& let ast::ExprKind::MacCall(m) = &expr.kind
&& cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err()
{
return ExpandResult::Retry(());
}
// Perform eager expansion on the expression.
// We want to be able to handle e.g., `concat!("foo", "bar")`.
@ -1379,10 +1426,10 @@ pub fn get_exprs_from_tts(
}
if p.token != token::Eof {
let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span });
return Err(guar);
return ExpandResult::Ready(Err(guar));
}
}
Ok(es)
ExpandResult::Ready(Ok(es))
}
pub fn parse_macro_name_and_helper_attrs(

View file

@ -659,7 +659,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
ExpandResult::Ready(match invoc.kind {
InvocationKind::Bang { mac, .. } => match ext {
InvocationKind::Bang { mac, span } => match ext {
SyntaxExtensionKind::Bang(expander) => {
match expander.expand(self.cx, span, mac.args.tokens.clone()) {
Ok(tok_result) => {
@ -669,7 +669,16 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
SyntaxExtensionKind::LegacyBang(expander) => {
let tok_result = expander.expand(self.cx, span, mac.args.tokens.clone());
let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) {
ExpandResult::Ready(tok_result) => tok_result,
ExpandResult::Retry(_) => {
// retry the original
return ExpandResult::Retry(Invocation {
kind: InvocationKind::Bang { mac, span },
..invoc
});
}
};
let result = if let Some(result) = fragment_kind.make_from(tok_result) {
result
} else {

View file

@ -1,5 +1,5 @@
use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
use crate::base::{SyntaxExtension, SyntaxExtensionKind};
use crate::base::{DummyResult, SyntaxExtension, SyntaxExtensionKind};
use crate::base::{ExpandResult, ExtCtxt, MacResult, MacroExpanderResult, TTMacroExpander};
use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind};
use crate::mbe;
use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg};
@ -111,8 +111,8 @@ impl TTMacroExpander for MacroRulesMacroExpander {
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx> {
expand_macro(
) -> MacroExpanderResult<'cx> {
ExpandResult::Ready(expand_macro(
cx,
sp,
self.span,
@ -122,7 +122,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
input,
&self.lhses,
&self.rhses,
)
))
}
}
@ -134,8 +134,8 @@ impl TTMacroExpander for DummyExpander {
_: &'cx mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> Box<dyn MacResult + 'cx> {
DummyResult::any(span, self.0)
) -> ExpandResult<Box<dyn MacResult + 'cx>, ()> {
ExpandResult::Ready(DummyResult::any(span, self.0))
}
}