use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
use thin_vec::{thin_vec, ThinVec};
macro path_local($x:ident) {
generic::ty::Path::new_local(sym::$x)
}
macro pathvec_std($($rest:ident)::+) {{
vec![ $( sym::$rest ),+ ]
}}
macro path_std($($x:tt)*) {
generic::ty::Path::new( pathvec_std!( $($x)* ) )
}
pub mod bounds;
pub mod clone;
pub mod debug;
pub mod decodable;
pub mod default;
pub mod encodable;
pub mod hash;
#[path = "cmp/eq.rs"]
pub mod eq;
#[path = "cmp/ord.rs"]
pub mod ord;
#[path = "cmp/partial_eq.rs"]
pub mod partial_eq;
#[path = "cmp/partial_ord.rs"]
pub mod partial_ord;
pub mod generic;
pub(crate) type BuiltinDeriveFn =
fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool);
pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn);
impl MultiItemModifier for BuiltinDerive {
fn expand(
&self,
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &MetaItem,
item: Annotatable,
is_derive_const: bool,
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
let span = ecx.with_def_site_ctxt(span);
let mut items = Vec::new();
match item {
Annotatable::Stmt(stmt) => {
if let ast::StmtKind::Item(item) = stmt.into_inner().kind {
(self.0)(
ecx,
span,
meta_item,
&Annotatable::Item(item),
&mut |a| {
items.push(Annotatable::Stmt(P(ast::Stmt {
id: ast::DUMMY_NODE_ID,
kind: ast::StmtKind::Item(a.expect_item()),
span,
})));
},
is_derive_const,
);
} else {
unreachable!("should have already errored on non-item statement")
}
}
_ => {
(self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const);
}
}
ExpandResult::Ready(items)
}
}
fn call_intrinsic(
cx: &ExtCtxt<'_>,
span: Span,
intrinsic: Symbol,
args: ThinVec<P<ast::Expr>>,
) -> P<ast::Expr> {
let span = cx.with_def_site_ctxt(span);
let path = cx.std_path(&[sym::intrinsics, intrinsic]);
cx.expr_call_global(span, path, args)
}
fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
let span = cx.with_def_site_ctxt(span);
let path = cx.std_path(&[sym::intrinsics, sym::unreachable]);
let call = cx.expr_call_global(span, path, ThinVec::new());
cx.expr_block(P(ast::Block {
stmts: thin_vec![cx.stmt_expr(call)],
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
span,
tokens: None,
could_be_bare_literal: false,
}))
}
fn inject_impl_of_structural_trait(
cx: &mut ExtCtxt<'_>,
span: Span,
item: &Annotatable,
structural_path: generic::ty::Path,
push: &mut dyn FnMut(Annotatable),
) {
let Annotatable::Item(item) = item else {
unreachable!();
};
let generics = match &item.kind {
ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics,
ItemKind::Union(..) => return,
_ => unreachable!(),
};
let mut generics = generics.clone();
let ctxt = span.ctxt();
let self_params: Vec<_> = generics
.params
.iter_mut()
.map(|param| match &mut param.kind {
ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident),
),
ast::GenericParamKind::Type { default } => {
*default = None;
ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
}
ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
*default = None;
ast::GenericArg::Const(
cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident),
)
}
})
.collect();
let type_ident = item.ident;
let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics));
let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params));
let mut attrs = ast::AttrVec::new();
attrs.extend(
item.attrs
.iter()
.filter(|a| {
[sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
.contains(&a.name_or_empty())
})
.cloned(),
);
attrs.push(cx.attr_word(sym::automatically_derived, span));
let newitem = cx.item(
span,
Ident::empty(),
attrs,
ItemKind::Impl(Box::new(Impl {
unsafety: ast::Unsafe::No,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
constness: ast::Const::No,
generics,
of_trait: Some(trait_ref),
self_ty: self_type,
items: ThinVec::new(),
})),
);
push(Annotatable::Item(newitem));
}
fn assert_ty_bounds(
cx: &mut ExtCtxt<'_>,
stmts: &mut ThinVec<ast::Stmt>,
ty: P<ast::Ty>,
span: Span,
assert_path: &[Symbol],
) {
let span = cx.with_def_site_ctxt(span);
let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
}