]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Builtin derives. |
2 | ||
3 | use base_db::{CrateOrigin, LangCrateOrigin}; | |
4 | use tracing::debug; | |
5 | ||
9ffffee4 | 6 | use crate::tt::{self, TokenId}; |
064997fb FG |
7 | use syntax::{ |
8 | ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName}, | |
9 | match_ast, | |
10 | }; | |
064997fb | 11 | |
353b0b11 | 12 | use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId}; |
064997fb FG |
13 | |
14 | macro_rules! register_builtin { | |
15 | ( $($trait:ident => $expand:ident),* ) => { | |
16 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | |
17 | pub enum BuiltinDeriveExpander { | |
18 | $($trait),* | |
19 | } | |
20 | ||
21 | impl BuiltinDeriveExpander { | |
22 | pub fn expand( | |
23 | &self, | |
353b0b11 | 24 | db: &dyn ExpandDatabase, |
064997fb FG |
25 | id: MacroCallId, |
26 | tt: &tt::Subtree, | |
27 | ) -> ExpandResult<tt::Subtree> { | |
28 | let expander = match *self { | |
29 | $( BuiltinDeriveExpander::$trait => $expand, )* | |
30 | }; | |
31 | expander(db, id, tt) | |
32 | } | |
33 | ||
34 | fn find_by_name(name: &name::Name) -> Option<Self> { | |
35 | match name { | |
36 | $( id if id == &name::name![$trait] => Some(BuiltinDeriveExpander::$trait), )* | |
37 | _ => None, | |
38 | } | |
39 | } | |
40 | } | |
41 | ||
42 | }; | |
43 | } | |
44 | ||
45 | register_builtin! { | |
46 | Copy => copy_expand, | |
47 | Clone => clone_expand, | |
48 | Default => default_expand, | |
49 | Debug => debug_expand, | |
50 | Hash => hash_expand, | |
51 | Ord => ord_expand, | |
52 | PartialOrd => partial_ord_expand, | |
53 | Eq => eq_expand, | |
54 | PartialEq => partial_eq_expand | |
55 | } | |
56 | ||
57 | pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> { | |
58 | BuiltinDeriveExpander::find_by_name(ident) | |
59 | } | |
60 | ||
61 | struct BasicAdtInfo { | |
62 | name: tt::Ident, | |
2b03887a FG |
63 | /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. |
64 | param_types: Vec<Option<tt::Subtree>>, | |
064997fb FG |
65 | } |
66 | ||
67 | fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> { | |
68 | let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems); | |
69 | let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| { | |
70 | debug!("derive node didn't parse"); | |
71 | ExpandError::Other("invalid item definition".into()) | |
72 | })?; | |
73 | let item = macro_items.items().next().ok_or_else(|| { | |
74 | debug!("no module item parsed"); | |
75 | ExpandError::Other("no item found".into()) | |
76 | })?; | |
77 | let node = item.syntax(); | |
78 | let (name, params) = match_ast! { | |
79 | match node { | |
80 | ast::Struct(it) => (it.name(), it.generic_param_list()), | |
81 | ast::Enum(it) => (it.name(), it.generic_param_list()), | |
82 | ast::Union(it) => (it.name(), it.generic_param_list()), | |
83 | _ => { | |
84 | debug!("unexpected node is {:?}", node); | |
85 | return Err(ExpandError::Other("expected struct, enum or union".into())) | |
86 | }, | |
87 | } | |
88 | }; | |
89 | let name = name.ok_or_else(|| { | |
90 | debug!("parsed item has no name"); | |
91 | ExpandError::Other("missing name".into()) | |
92 | })?; | |
93 | let name_token_id = | |
94 | token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); | |
9ffffee4 | 95 | let name_token = tt::Ident { span: name_token_id, text: name.text().into() }; |
2b03887a FG |
96 | let param_types = params |
97 | .into_iter() | |
98 | .flat_map(|param_list| param_list.type_or_const_params()) | |
99 | .map(|param| { | |
100 | if let ast::TypeOrConstParam::Const(param) = param { | |
101 | let ty = param | |
102 | .ty() | |
103 | .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) | |
9ffffee4 | 104 | .unwrap_or_else(tt::Subtree::empty); |
2b03887a FG |
105 | Some(ty) |
106 | } else { | |
107 | None | |
108 | } | |
064997fb | 109 | }) |
2b03887a FG |
110 | .collect(); |
111 | Ok(BasicAdtInfo { name: name_token, param_types }) | |
064997fb FG |
112 | } |
113 | ||
114 | fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> { | |
115 | let info = match parse_adt(tt) { | |
116 | Ok(info) => info, | |
9ffffee4 | 117 | Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e), |
064997fb | 118 | }; |
2b03887a FG |
119 | let (params, args): (Vec<_>, Vec<_>) = info |
120 | .param_types | |
121 | .into_iter() | |
122 | .enumerate() | |
123 | .map(|(idx, param_ty)| { | |
124 | let ident = tt::Leaf::Ident(tt::Ident { | |
9ffffee4 | 125 | span: tt::TokenId::unspecified(), |
2b03887a FG |
126 | text: format!("T{idx}").into(), |
127 | }); | |
128 | let ident_ = ident.clone(); | |
129 | if let Some(ty) = param_ty { | |
130 | (quote! { const #ident : #ty , }, quote! { #ident_ , }) | |
131 | } else { | |
132 | let bound = trait_path.clone(); | |
133 | (quote! { #ident : #bound , }, quote! { #ident_ , }) | |
134 | } | |
135 | }) | |
136 | .unzip(); | |
064997fb | 137 | let name = info.name; |
064997fb | 138 | let expanded = quote! { |
2b03887a | 139 | impl < ##params > #trait_path for #name < ##args > {} |
064997fb FG |
140 | }; |
141 | ExpandResult::ok(expanded) | |
142 | } | |
143 | ||
353b0b11 | 144 | fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree { |
064997fb FG |
145 | // FIXME: make hygiene works for builtin derive macro |
146 | // such that $crate can be used here. | |
147 | let cg = db.crate_graph(); | |
148 | let krate = db.lookup_intern_macro_call(id).krate; | |
149 | ||
150 | let tt = if matches!(cg[krate].origin, CrateOrigin::Lang(LangCrateOrigin::Core)) { | |
151 | cov_mark::hit!(test_copy_expand_in_core); | |
152 | quote! { crate } | |
153 | } else { | |
154 | quote! { core } | |
155 | }; | |
156 | ||
157 | tt.token_trees[0].clone() | |
158 | } | |
159 | ||
160 | fn copy_expand( | |
353b0b11 | 161 | db: &dyn ExpandDatabase, |
064997fb FG |
162 | id: MacroCallId, |
163 | tt: &tt::Subtree, | |
164 | ) -> ExpandResult<tt::Subtree> { | |
165 | let krate = find_builtin_crate(db, id); | |
166 | expand_simple_derive(tt, quote! { #krate::marker::Copy }) | |
167 | } | |
168 | ||
169 | fn clone_expand( | |
353b0b11 | 170 | db: &dyn ExpandDatabase, |
064997fb FG |
171 | id: MacroCallId, |
172 | tt: &tt::Subtree, | |
173 | ) -> ExpandResult<tt::Subtree> { | |
174 | let krate = find_builtin_crate(db, id); | |
175 | expand_simple_derive(tt, quote! { #krate::clone::Clone }) | |
176 | } | |
177 | ||
178 | fn default_expand( | |
353b0b11 | 179 | db: &dyn ExpandDatabase, |
064997fb FG |
180 | id: MacroCallId, |
181 | tt: &tt::Subtree, | |
182 | ) -> ExpandResult<tt::Subtree> { | |
183 | let krate = find_builtin_crate(db, id); | |
184 | expand_simple_derive(tt, quote! { #krate::default::Default }) | |
185 | } | |
186 | ||
187 | fn debug_expand( | |
353b0b11 | 188 | db: &dyn ExpandDatabase, |
064997fb FG |
189 | id: MacroCallId, |
190 | tt: &tt::Subtree, | |
191 | ) -> ExpandResult<tt::Subtree> { | |
192 | let krate = find_builtin_crate(db, id); | |
193 | expand_simple_derive(tt, quote! { #krate::fmt::Debug }) | |
194 | } | |
195 | ||
196 | fn hash_expand( | |
353b0b11 | 197 | db: &dyn ExpandDatabase, |
064997fb FG |
198 | id: MacroCallId, |
199 | tt: &tt::Subtree, | |
200 | ) -> ExpandResult<tt::Subtree> { | |
201 | let krate = find_builtin_crate(db, id); | |
202 | expand_simple_derive(tt, quote! { #krate::hash::Hash }) | |
203 | } | |
204 | ||
353b0b11 FG |
205 | fn eq_expand( |
206 | db: &dyn ExpandDatabase, | |
207 | id: MacroCallId, | |
208 | tt: &tt::Subtree, | |
209 | ) -> ExpandResult<tt::Subtree> { | |
064997fb FG |
210 | let krate = find_builtin_crate(db, id); |
211 | expand_simple_derive(tt, quote! { #krate::cmp::Eq }) | |
212 | } | |
213 | ||
214 | fn partial_eq_expand( | |
353b0b11 | 215 | db: &dyn ExpandDatabase, |
064997fb FG |
216 | id: MacroCallId, |
217 | tt: &tt::Subtree, | |
218 | ) -> ExpandResult<tt::Subtree> { | |
219 | let krate = find_builtin_crate(db, id); | |
220 | expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }) | |
221 | } | |
222 | ||
223 | fn ord_expand( | |
353b0b11 | 224 | db: &dyn ExpandDatabase, |
064997fb FG |
225 | id: MacroCallId, |
226 | tt: &tt::Subtree, | |
227 | ) -> ExpandResult<tt::Subtree> { | |
228 | let krate = find_builtin_crate(db, id); | |
229 | expand_simple_derive(tt, quote! { #krate::cmp::Ord }) | |
230 | } | |
231 | ||
232 | fn partial_ord_expand( | |
353b0b11 | 233 | db: &dyn ExpandDatabase, |
064997fb FG |
234 | id: MacroCallId, |
235 | tt: &tt::Subtree, | |
236 | ) -> ExpandResult<tt::Subtree> { | |
237 | let krate = find_builtin_crate(db, id); | |
238 | expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) | |
239 | } |