3 use base_db::{CrateOrigin, LangCrateOrigin};
7 ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
12 use crate::{db::AstDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
14 macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => {
16 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17 pub enum BuiltinDeriveExpander {
21 impl BuiltinDeriveExpander {
27 ) -> ExpandResult<tt::Subtree> {
28 let expander = match *self {
29 $( BuiltinDeriveExpander::$trait => $expand, )*
34 fn find_by_name(name: &name::Name) -> Option<Self> {
36 $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
47 Clone => clone_expand,
48 Default => default_expand,
49 Debug => debug_expand,
52 PartialOrd => partial_ord_expand,
54 PartialEq => partial_eq_expand
57 pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> {
58 BuiltinDeriveExpander::find_by_name(ident)
63 type_or_const_params: usize,
66 fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
67 let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
68 let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
69 debug!("derive node didn't parse");
70 ExpandError::Other("invalid item definition".into())
72 let item = macro_items.items().next().ok_or_else(|| {
73 debug!("no module item parsed");
74 ExpandError::Other("no item found".into())
76 let node = item.syntax();
77 let (name, params) = match_ast! {
79 ast::Struct(it) => (it.name(), it.generic_param_list()),
80 ast::Enum(it) => (it.name(), it.generic_param_list()),
81 ast::Union(it) => (it.name(), it.generic_param_list()),
83 debug!("unexpected node is {:?}", node);
84 return Err(ExpandError::Other("expected struct, enum or union".into()))
88 let name = name.ok_or_else(|| {
89 debug!("parsed item has no name");
90 ExpandError::Other("missing name".into())
93 token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
94 let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
95 let type_or_const_params =
96 params.map_or(0, |type_param_list| type_param_list.type_or_const_params().count());
97 Ok(BasicAdtInfo { name: name_token, type_or_const_params })
100 fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
101 let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
103 tt::Leaf::Punct(tt::Punct {
105 spacing: tt::Spacing::Alone,
106 id: tt::TokenId::unspecified(),
113 tt::Leaf::Punct(tt::Punct {
115 spacing: tt::Spacing::Alone,
116 id: tt::TokenId::unspecified(),
122 tt::Leaf::Ident(tt::Ident {
123 id: tt::TokenId::unspecified(),
124 text: format!("T{}", i).into(),
128 result.extend(bound.iter().cloned());
131 tt::Leaf::Punct(tt::Punct {
133 spacing: tt::Spacing::Alone,
134 id: tt::TokenId::unspecified(),
141 fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
142 let info = match parse_adt(tt) {
144 Err(e) => return ExpandResult::only_err(e),
146 let name = info.name;
147 let trait_path_clone = trait_path.token_trees.clone();
148 let bound = (quote! { : ##trait_path_clone }).token_trees;
149 let type_params = make_type_args(info.type_or_const_params, bound);
150 let type_args = make_type_args(info.type_or_const_params, Vec::new());
151 let trait_path = trait_path.token_trees;
152 let expanded = quote! {
153 impl ##type_params ##trait_path for #name ##type_args {}
155 ExpandResult::ok(expanded)
158 fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree {
159 // FIXME: make hygiene works for builtin derive macro
160 // such that $crate can be used here.
161 let cg = db.crate_graph();
162 let krate = db.lookup_intern_macro_call(id).krate;
164 let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) {
165 cov_mark::hit!(test_copy_expand_in_core);
171 tt.token_trees[0].clone()
175 db: &dyn AstDatabase,
178 ) -> ExpandResult<tt::Subtree> {
179 let krate = find_builtin_crate(db, id);
180 expand_simple_derive(tt, quote! { #krate::marker::Copy })
184 db: &dyn AstDatabase,
187 ) -> ExpandResult<tt::Subtree> {
188 let krate = find_builtin_crate(db, id);
189 expand_simple_derive(tt, quote! { #krate::clone::Clone })
193 db: &dyn AstDatabase,
196 ) -> ExpandResult<tt::Subtree> {
197 let krate = find_builtin_crate(db, id);
198 expand_simple_derive(tt, quote! { #krate::default::Default })
202 db: &dyn AstDatabase,
205 ) -> ExpandResult<tt::Subtree> {
206 let krate = find_builtin_crate(db, id);
207 expand_simple_derive(tt, quote! { #krate::fmt::Debug })
211 db: &dyn AstDatabase,
214 ) -> ExpandResult<tt::Subtree> {
215 let krate = find_builtin_crate(db, id);
216 expand_simple_derive(tt, quote! { #krate::hash::Hash })
219 fn eq_expand(db: &dyn AstDatabase, id: MacroCallId, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
220 let krate = find_builtin_crate(db, id);
221 expand_simple_derive(tt, quote! { #krate::cmp::Eq })
224 fn partial_eq_expand(
225 db: &dyn AstDatabase,
228 ) -> ExpandResult<tt::Subtree> {
229 let krate = find_builtin_crate(db, id);
230 expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
234 db: &dyn AstDatabase,
237 ) -> ExpandResult<tt::Subtree> {
238 let krate = find_builtin_crate(db, id);
239 expand_simple_derive(tt, quote! { #krate::cmp::Ord })
242 fn partial_ord_expand(
243 db: &dyn AstDatabase,
246 ) -> ExpandResult<tt::Subtree> {
247 let krate = find_builtin_crate(db, id);
248 expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })