split up expansion code of eii macro into functions
This commit is contained in:
parent
0f0d850e91
commit
6608f6ace7
1 changed files with 244 additions and 172 deletions
|
|
@ -1,11 +1,12 @@
|
|||
use rustc_ast::token::{Delimiter, TokenKind};
|
||||
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
|
||||
use rustc_ast::{
|
||||
DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream,
|
||||
Attribute, DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, MetaItem, Path, Stmt, StmtKind,
|
||||
Visibility, ast,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::path_to_string;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt};
|
||||
use rustc_span::{Ident, Span, kw, sym};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
|
||||
use thin_vec::{ThinVec, thin_vec};
|
||||
|
||||
use crate::errors::{
|
||||
|
|
@ -52,12 +53,12 @@ pub(crate) fn unsafe_eii(
|
|||
|
||||
fn eii_(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
eii_attr_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
impl_unsafe: bool,
|
||||
) -> Vec<Annotatable> {
|
||||
let span = ecx.with_def_site_ctxt(span);
|
||||
let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span);
|
||||
|
||||
let (item, stmt) = if let Annotatable::Item(item) = item {
|
||||
(item, false)
|
||||
|
|
@ -67,7 +68,7 @@ fn eii_(
|
|||
(item.clone(), true)
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
span: eii_attr_span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![item];
|
||||
|
|
@ -77,113 +78,218 @@ fn eii_(
|
|||
|
||||
let item = *item;
|
||||
|
||||
let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } =
|
||||
item
|
||||
else {
|
||||
let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = item else {
|
||||
ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
|
||||
span,
|
||||
span: eii_attr_span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
return vec![Annotatable::Item(Box::new(item))];
|
||||
};
|
||||
|
||||
// Detect when this is the *second* eii attribute on an item.
|
||||
let mut new_attrs = ThinVec::new();
|
||||
for i in attrs {
|
||||
if i.has_name(sym::eii) {
|
||||
ecx.dcx().emit_err(EiiOnlyOnce {
|
||||
span: i.span,
|
||||
first_span: span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
} else {
|
||||
new_attrs.push(i);
|
||||
}
|
||||
}
|
||||
let attrs = new_attrs;
|
||||
let attrs_from_decl =
|
||||
filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path);
|
||||
|
||||
let macro_name = if meta_item.is_word() {
|
||||
func.ident
|
||||
} else if let Some([first]) = meta_item.meta_item_list()
|
||||
&& let Some(m) = first.meta_item()
|
||||
&& m.path.segments.len() == 1
|
||||
{
|
||||
m.path.segments[0].ident
|
||||
} else {
|
||||
ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
|
||||
span: meta_item.span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
});
|
||||
let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else {
|
||||
return vec![Annotatable::Item(orig_item)];
|
||||
};
|
||||
|
||||
// span of the declaring item without attributes
|
||||
let item_span = func.sig.span;
|
||||
// span of the eii attribute and the item below it, i.e. the full declaration
|
||||
let decl_span = eii_attr_span.to(item_span);
|
||||
let foreign_item_name = func.ident;
|
||||
|
||||
let mut return_items = Vec::new();
|
||||
|
||||
if func.body.is_some() {
|
||||
let mut default_func = func.clone();
|
||||
func.body = None;
|
||||
default_func.eii_impls.push(ast::EiiImpl {
|
||||
node_id: DUMMY_NODE_ID,
|
||||
eii_macro_path: ast::Path::from_ident(macro_name),
|
||||
impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default },
|
||||
span,
|
||||
inner_span: macro_name.span,
|
||||
is_default: true, // important!
|
||||
});
|
||||
|
||||
return_items.push(Box::new(ast::Item {
|
||||
attrs: ThinVec::new(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
|
||||
kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
|
||||
ident: Ident { name: kw::Underscore, span },
|
||||
defaultness: ast::Defaultness::Final,
|
||||
generics: ast::Generics::default(),
|
||||
ty: Box::new(ast::Ty {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::TyKind::Tup(ThinVec::new()),
|
||||
span,
|
||||
tokens: None,
|
||||
}),
|
||||
rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::ExprKind::Block(
|
||||
Box::new(ast::Block {
|
||||
stmts: thin_vec![ast::Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::StmtKind::Item(Box::new(ast::Item {
|
||||
attrs: thin_vec![], // FIXME: re-add some original attrs
|
||||
id: DUMMY_NODE_ID,
|
||||
span: item_span,
|
||||
vis: ast::Visibility {
|
||||
span,
|
||||
kind: ast::VisibilityKind::Inherited,
|
||||
tokens: None
|
||||
},
|
||||
kind: ItemKind::Fn(default_func),
|
||||
tokens: None,
|
||||
})),
|
||||
span
|
||||
}],
|
||||
id: DUMMY_NODE_ID,
|
||||
rules: ast::BlockCheckMode::Default,
|
||||
span,
|
||||
tokens: None,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
span,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None,
|
||||
}))),
|
||||
define_opaque: None,
|
||||
})),
|
||||
tokens: None,
|
||||
}))
|
||||
return_items.push(Box::new(generate_default_impl(
|
||||
&func,
|
||||
impl_unsafe,
|
||||
macro_name,
|
||||
eii_attr_span,
|
||||
item_span,
|
||||
)))
|
||||
}
|
||||
|
||||
let decl_span = span.to(func.sig.span);
|
||||
return_items.push(Box::new(generate_foreign_item(
|
||||
ecx,
|
||||
eii_attr_span,
|
||||
item_span,
|
||||
*func,
|
||||
vis,
|
||||
&attrs_from_decl,
|
||||
)));
|
||||
return_items.push(Box::new(generate_attribute_macro_to_implement(
|
||||
ecx,
|
||||
eii_attr_span,
|
||||
macro_name,
|
||||
foreign_item_name,
|
||||
impl_unsafe,
|
||||
decl_span,
|
||||
)));
|
||||
|
||||
if stmt {
|
||||
return_items
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
Annotatable::Stmt(Box::new(Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: StmtKind::Item(i),
|
||||
span: eii_attr_span,
|
||||
}))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Decide on the name of the macro that can be used to implement the EII.
|
||||
/// This is either an explicitly given name, or the name of the item in the
|
||||
/// declaration of the EII.
|
||||
fn name_for_impl_macro(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
func: &ast::Fn,
|
||||
meta_item: &MetaItem,
|
||||
) -> Result<Ident, ErrorGuaranteed> {
|
||||
if meta_item.is_word() {
|
||||
Ok(func.ident)
|
||||
} else if let Some([first]) = meta_item.meta_item_list()
|
||||
&& let Some(m) = first.meta_item()
|
||||
&& m.path.segments.len() == 1
|
||||
{
|
||||
Ok(m.path.segments[0].ident)
|
||||
} else {
|
||||
Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
|
||||
span: meta_item.span,
|
||||
name: path_to_string(&meta_item.path),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that in the list of attrs, there's only a single `eii` attribute.
|
||||
fn filter_attrs_for_multiple_eii_attr(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
attrs: ThinVec<Attribute>,
|
||||
eii_attr_span: Span,
|
||||
eii_attr_path: &Path,
|
||||
) -> ThinVec<Attribute> {
|
||||
attrs
|
||||
.into_iter()
|
||||
.filter(|i| {
|
||||
if i.has_name(sym::eii) {
|
||||
ecx.dcx().emit_err(EiiOnlyOnce {
|
||||
span: i.span,
|
||||
first_span: eii_attr_span,
|
||||
name: path_to_string(eii_attr_path),
|
||||
});
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_default_impl(
|
||||
func: &ast::Fn,
|
||||
impl_unsafe: bool,
|
||||
macro_name: Ident,
|
||||
eii_attr_span: Span,
|
||||
item_span: Span,
|
||||
) -> ast::Item {
|
||||
// FIXME: re-add some original attrs
|
||||
let attrs = ThinVec::new();
|
||||
|
||||
let mut default_func = func.clone();
|
||||
default_func.eii_impls.push(EiiImpl {
|
||||
node_id: DUMMY_NODE_ID,
|
||||
inner_span: macro_name.span,
|
||||
eii_macro_path: ast::Path::from_ident(macro_name),
|
||||
impl_safety: if impl_unsafe {
|
||||
ast::Safety::Unsafe(eii_attr_span)
|
||||
} else {
|
||||
ast::Safety::Default
|
||||
},
|
||||
span: eii_attr_span,
|
||||
is_default: true,
|
||||
});
|
||||
|
||||
ast::Item {
|
||||
attrs: ThinVec::new(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: eii_attr_span,
|
||||
vis: ast::Visibility {
|
||||
span: eii_attr_span,
|
||||
kind: ast::VisibilityKind::Inherited,
|
||||
tokens: None,
|
||||
},
|
||||
kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
|
||||
ident: Ident { name: kw::Underscore, span: eii_attr_span },
|
||||
defaultness: ast::Defaultness::Final,
|
||||
generics: ast::Generics::default(),
|
||||
ty: Box::new(ast::Ty {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::TyKind::Tup(ThinVec::new()),
|
||||
span: eii_attr_span,
|
||||
tokens: None,
|
||||
}),
|
||||
rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::ExprKind::Block(
|
||||
Box::new(ast::Block {
|
||||
stmts: thin_vec![ast::Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: ast::StmtKind::Item(Box::new(ast::Item {
|
||||
attrs,
|
||||
id: DUMMY_NODE_ID,
|
||||
span: item_span,
|
||||
vis: ast::Visibility {
|
||||
span: eii_attr_span,
|
||||
kind: ast::VisibilityKind::Inherited,
|
||||
tokens: None
|
||||
},
|
||||
kind: ItemKind::Fn(Box::new(default_func)),
|
||||
tokens: None,
|
||||
})),
|
||||
span: eii_attr_span
|
||||
}],
|
||||
id: DUMMY_NODE_ID,
|
||||
rules: ast::BlockCheckMode::Default,
|
||||
span: eii_attr_span,
|
||||
tokens: None,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
span: eii_attr_span,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None,
|
||||
}))),
|
||||
define_opaque: None,
|
||||
})),
|
||||
tokens: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a foreign item, like
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// extern "…" { safe fn item(); }
|
||||
/// ```
|
||||
fn generate_foreign_item(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
eii_attr_span: Span,
|
||||
item_span: Span,
|
||||
mut func: ast::Fn,
|
||||
vis: Visibility,
|
||||
attrs_from_decl: &[Attribute],
|
||||
) -> ast::Item {
|
||||
let mut foreign_item_attrs = ThinVec::new();
|
||||
foreign_item_attrs.extend_from_slice(attrs_from_decl);
|
||||
|
||||
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
|
||||
// This attribute makes sure that we later know that this foreign item's symbol should not be.
|
||||
foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_extern_item, eii_attr_span));
|
||||
|
||||
let abi = match func.sig.header.ext {
|
||||
// extern "X" fn => extern "X" {}
|
||||
|
|
@ -196,85 +302,69 @@ fn eii_(
|
|||
suffix: None,
|
||||
symbol_unescaped: sym::Rust,
|
||||
style: ast::StrStyle::Cooked,
|
||||
span,
|
||||
span: eii_attr_span,
|
||||
}),
|
||||
};
|
||||
|
||||
// ABI has been moved to the extern {} block, so we remove it from the fn item.
|
||||
func.sig.header.ext = ast::Extern::None;
|
||||
func.body = None;
|
||||
|
||||
// And mark safe functions explicitly as `safe fn`.
|
||||
if func.sig.header.safety == ast::Safety::Default {
|
||||
func.sig.header.safety = ast::Safety::Safe(func.sig.span);
|
||||
}
|
||||
|
||||
// extern "…" { safe fn item(); }
|
||||
let mut extern_item_attrs = attrs.clone();
|
||||
extern_item_attrs.push(ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
|
||||
item: ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
// Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled.
|
||||
// This attribute makes sure that we later know that this foreign item's symbol should not be.
|
||||
path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
|
||||
args: ast::AttrArgs::Empty,
|
||||
tokens: None,
|
||||
},
|
||||
tokens: None,
|
||||
})),
|
||||
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
});
|
||||
|
||||
let extern_block = Box::new(ast::Item {
|
||||
ast::Item {
|
||||
attrs: ast::AttrVec::default(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
|
||||
span: eii_attr_span,
|
||||
vis: ast::Visibility {
|
||||
span: eii_attr_span,
|
||||
kind: ast::VisibilityKind::Inherited,
|
||||
tokens: None,
|
||||
},
|
||||
kind: ast::ItemKind::ForeignMod(ast::ForeignMod {
|
||||
extern_span: span,
|
||||
safety: ast::Safety::Unsafe(span),
|
||||
extern_span: eii_attr_span,
|
||||
safety: ast::Safety::Unsafe(eii_attr_span),
|
||||
abi,
|
||||
items: From::from([Box::new(ast::ForeignItem {
|
||||
attrs: extern_item_attrs,
|
||||
attrs: foreign_item_attrs,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: item_span,
|
||||
vis,
|
||||
kind: ast::ForeignItemKind::Fn(func.clone()),
|
||||
kind: ast::ForeignItemKind::Fn(Box::new(func.clone())),
|
||||
tokens: None,
|
||||
})]),
|
||||
}),
|
||||
tokens: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut macro_attrs = attrs.clone();
|
||||
macro_attrs.push(
|
||||
// #[builtin_macro(eii_shared_macro)]
|
||||
ast::Attribute {
|
||||
kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
|
||||
item: ast::AttrItem {
|
||||
unsafety: ast::Safety::Default,
|
||||
path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)),
|
||||
args: ast::AttrArgs::Delimited(ast::DelimArgs {
|
||||
dspan: DelimSpan::from_single(span),
|
||||
delim: Delimiter::Parenthesis,
|
||||
tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone(
|
||||
token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No),
|
||||
span,
|
||||
)]),
|
||||
}),
|
||||
tokens: None,
|
||||
},
|
||||
tokens: None,
|
||||
})),
|
||||
id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
|
||||
style: ast::AttrStyle::Outer,
|
||||
span,
|
||||
},
|
||||
);
|
||||
/// Generate a stub macro (a bit like in core) that will roughly look like:
|
||||
///
|
||||
/// ```rust, ignore, example
|
||||
/// // Since this a stub macro, the actual code that expands it lives in the compiler.
|
||||
/// // This attribute tells the compiler that
|
||||
/// #[builtin_macro(eii_shared_macro)]
|
||||
/// // the metadata to link this macro to the generated foreign item.
|
||||
/// #[eii_extern_target(<related_reign_item>)]
|
||||
/// macro macro_name { () => {} }
|
||||
/// ```
|
||||
fn generate_attribute_macro_to_implement(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
macro_name: Ident,
|
||||
foreign_item_name: Ident,
|
||||
impl_unsafe: bool,
|
||||
decl_span: Span,
|
||||
) -> ast::Item {
|
||||
let mut macro_attrs = ThinVec::new();
|
||||
|
||||
let macro_def = Box::new(ast::Item {
|
||||
// #[builtin_macro(eii_shared_macro)]
|
||||
macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
|
||||
|
||||
ast::Item {
|
||||
attrs: macro_attrs,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span,
|
||||
|
|
@ -305,33 +395,15 @@ fn eii_(
|
|||
]),
|
||||
}),
|
||||
macro_rules: false,
|
||||
// #[eii_extern_target(func.ident)]
|
||||
// #[eii_extern_target(foreign_item_ident)]
|
||||
eii_extern_target: Some(ast::EiiExternTarget {
|
||||
extern_item_path: ast::Path::from_ident(func.ident),
|
||||
extern_item_path: ast::Path::from_ident(foreign_item_name),
|
||||
impl_unsafe,
|
||||
span: decl_span,
|
||||
}),
|
||||
},
|
||||
),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
return_items.push(extern_block);
|
||||
return_items.push(macro_def);
|
||||
|
||||
if stmt {
|
||||
return_items
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
Annotatable::Stmt(Box::new(Stmt {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: StmtKind::Item(i),
|
||||
span,
|
||||
}))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -436,10 +508,10 @@ pub(crate) fn eii_shared_macro(
|
|||
|
||||
f.eii_impls.push(EiiImpl {
|
||||
node_id: DUMMY_NODE_ID,
|
||||
inner_span: meta_item.path.span,
|
||||
eii_macro_path: meta_item.path.clone(),
|
||||
impl_safety: meta_item.unsafety,
|
||||
span,
|
||||
inner_span: meta_item.path.span,
|
||||
is_default,
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue