]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-expand / src / builtin_derive_macro.rs
CommitLineData
064997fb
FG
1//! Builtin derives.
2
3use base_db::{CrateOrigin, LangCrateOrigin};
4use tracing::debug;
5
9ffffee4 6use crate::tt::{self, TokenId};
064997fb
FG
7use syntax::{
8 ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
9 match_ast,
10};
064997fb 11
353b0b11 12use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
064997fb
FG
13
14macro_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
45register_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
57pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> {
58 BuiltinDeriveExpander::find_by_name(ident)
59}
60
61struct 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
67fn 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
114fn 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 144fn 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
160fn 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
169fn 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
178fn 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
187fn 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
196fn 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
205fn 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
214fn 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
223fn 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
232fn 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}