]> git.lizzy.rs Git - rust.git/blob - crates/hir_expand/src/builtin_derive_macro.rs
Rename intern_macro -> intern_macro_call
[rust.git] / crates / hir_expand / src / builtin_derive_macro.rs
1 //! Builtin derives.
2
3 use tracing::debug;
4
5 use mbe::ExpandResult;
6 use syntax::{
7     ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
8     match_ast,
9 };
10
11 use crate::{db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
12
13 macro_rules! register_builtin {
14     ( $($trait:ident => $expand:ident),* ) => {
15         #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16         pub enum BuiltinDeriveExpander {
17             $($trait),*
18         }
19
20         impl BuiltinDeriveExpander {
21             pub fn expand(
22                 &self,
23                 db: &dyn AstDatabase,
24                 id: MacroCallId,
25                 tt: &tt::Subtree,
26             ) -> ExpandResult<tt::Subtree> {
27                 let expander = match *self {
28                     $( BuiltinDeriveExpander::$trait => $expand, )*
29                 };
30                 expander(db, id, tt)
31             }
32
33             fn find_by_name(name: &name::Name) -> Option<Self> {
34                 match name {
35                     $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )*
36                      _ => None,
37                 }
38             }
39         }
40
41     };
42 }
43
44 register_builtin! {
45     Copy => copy_expand,
46     Clone => clone_expand,
47     Default => default_expand,
48     Debug => debug_expand,
49     Hash => hash_expand,
50     Ord => ord_expand,
51     PartialOrd => partial_ord_expand,
52     Eq => eq_expand,
53     PartialEq => partial_eq_expand
54 }
55
56 pub fn find_builtin_derive(
57     ident: &name::Name,
58     krate: CrateId,
59     ast_id: AstId<ast::Macro>,
60 ) -> Option<MacroDefId> {
61     let expander = BuiltinDeriveExpander::find_by_name(ident)?;
62     Some(MacroDefId {
63         krate,
64         kind: MacroDefKind::BuiltInDerive(expander, ast_id),
65         local_inner: false,
66     })
67 }
68
69 struct BasicAdtInfo {
70     name: tt::Ident,
71     type_params: usize,
72 }
73
74 fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
75     let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::Items)?; // FragmentKind::Items doesn't parse attrs?
76     let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
77         debug!("derive node didn't parse");
78         mbe::ExpandError::UnexpectedToken
79     })?;
80     let item = macro_items.items().next().ok_or_else(|| {
81         debug!("no module item parsed");
82         mbe::ExpandError::NoMatchingRule
83     })?;
84     let node = item.syntax();
85     let (name, params) = match_ast! {
86         match node {
87             ast::Struct(it) => (it.name(), it.generic_param_list()),
88             ast::Enum(it) => (it.name(), it.generic_param_list()),
89             ast::Union(it) => (it.name(), it.generic_param_list()),
90             _ => {
91                 debug!("unexpected node is {:?}", node);
92                 return Err(mbe::ExpandError::ConversionError)
93             },
94         }
95     };
96     let name = name.ok_or_else(|| {
97         debug!("parsed item has no name");
98         mbe::ExpandError::NoMatchingRule
99     })?;
100     let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
101         debug!("name token not found");
102         mbe::ExpandError::ConversionError
103     })?;
104     let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
105     let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
106     Ok(BasicAdtInfo { name: name_token, type_params })
107 }
108
109 fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
110     let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
111     result.push(
112         tt::Leaf::Punct(tt::Punct {
113             char: '<',
114             spacing: tt::Spacing::Alone,
115             id: tt::TokenId::unspecified(),
116         })
117         .into(),
118     );
119     for i in 0..n {
120         if i > 0 {
121             result.push(
122                 tt::Leaf::Punct(tt::Punct {
123                     char: ',',
124                     spacing: tt::Spacing::Alone,
125                     id: tt::TokenId::unspecified(),
126                 })
127                 .into(),
128             );
129         }
130         result.push(
131             tt::Leaf::Ident(tt::Ident {
132                 id: tt::TokenId::unspecified(),
133                 text: format!("T{}", i).into(),
134             })
135             .into(),
136         );
137         result.extend(bound.iter().cloned());
138     }
139     result.push(
140         tt::Leaf::Punct(tt::Punct {
141             char: '>',
142             spacing: tt::Spacing::Alone,
143             id: tt::TokenId::unspecified(),
144         })
145         .into(),
146     );
147     result
148 }
149
150 fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
151     let info = match parse_adt(tt) {
152         Ok(info) => info,
153         Err(e) => return ExpandResult::only_err(e),
154     };
155     let name = info.name;
156     let trait_path_clone = trait_path.token_trees.clone();
157     let bound = (quote! { : ##trait_path_clone }).token_trees;
158     let type_params = make_type_args(info.type_params, bound);
159     let type_args = make_type_args(info.type_params, Vec::new());
160     let trait_path = trait_path.token_trees;
161     let expanded = quote! {
162         impl ##type_params ##trait_path for #name ##type_args {}
163     };
164     ExpandResult::ok(expanded)
165 }
166
167 fn find_builtin_crate(db: &dyn AstDatabase, id: MacroCallId) -> tt::TokenTree {
168     // FIXME: make hygiene works for builtin derive macro
169     // such that $crate can be used here.
170     let cg = db.crate_graph();
171     let krate = db.lookup_intern_macro_call(id).krate;
172
173     // XXX
174     //  All crates except core itself should have a dependency on core,
175     //  We detect `core` by seeing whether it doesn't have such a dependency.
176     let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
177         quote! { core }
178     } else {
179         cov_mark::hit!(test_copy_expand_in_core);
180         quote! { crate }
181     };
182
183     tt.token_trees[0].clone()
184 }
185
186 fn copy_expand(
187     db: &dyn AstDatabase,
188     id: MacroCallId,
189     tt: &tt::Subtree,
190 ) -> ExpandResult<tt::Subtree> {
191     let krate = find_builtin_crate(db, id);
192     expand_simple_derive(tt, quote! { #krate::marker::Copy })
193 }
194
195 fn clone_expand(
196     db: &dyn AstDatabase,
197     id: MacroCallId,
198     tt: &tt::Subtree,
199 ) -> ExpandResult<tt::Subtree> {
200     let krate = find_builtin_crate(db, id);
201     expand_simple_derive(tt, quote! { #krate::clone::Clone })
202 }
203
204 fn default_expand(
205     db: &dyn AstDatabase,
206     id: MacroCallId,
207     tt: &tt::Subtree,
208 ) -> ExpandResult<tt::Subtree> {
209     let krate = find_builtin_crate(db, id);
210     expand_simple_derive(tt, quote! { #krate::default::Default })
211 }
212
213 fn debug_expand(
214     db: &dyn AstDatabase,
215     id: MacroCallId,
216     tt: &tt::Subtree,
217 ) -> ExpandResult<tt::Subtree> {
218     let krate = find_builtin_crate(db, id);
219     expand_simple_derive(tt, quote! { #krate::fmt::Debug })
220 }
221
222 fn hash_expand(
223     db: &dyn AstDatabase,
224     id: MacroCallId,
225     tt: &tt::Subtree,
226 ) -> ExpandResult<tt::Subtree> {
227     let krate = find_builtin_crate(db, id);
228     expand_simple_derive(tt, quote! { #krate::hash::Hash })
229 }
230
231 fn eq_expand(db: &dyn AstDatabase, id: MacroCallId, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
232     let krate = find_builtin_crate(db, id);
233     expand_simple_derive(tt, quote! { #krate::cmp::Eq })
234 }
235
236 fn partial_eq_expand(
237     db: &dyn AstDatabase,
238     id: MacroCallId,
239     tt: &tt::Subtree,
240 ) -> ExpandResult<tt::Subtree> {
241     let krate = find_builtin_crate(db, id);
242     expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
243 }
244
245 fn ord_expand(
246     db: &dyn AstDatabase,
247     id: MacroCallId,
248     tt: &tt::Subtree,
249 ) -> ExpandResult<tt::Subtree> {
250     let krate = find_builtin_crate(db, id);
251     expand_simple_derive(tt, quote! { #krate::cmp::Ord })
252 }
253
254 fn partial_ord_expand(
255     db: &dyn AstDatabase,
256     id: MacroCallId,
257     tt: &tt::Subtree,
258 ) -> ExpandResult<tt::Subtree> {
259     let krate = find_builtin_crate(db, id);
260     expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
261 }