]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/expand_macro.rs
fix: Fix expand_macro always expanding the first listed derive
[rust.git] / crates / ide / src / expand_macro.rs
1 use hir::Semantics;
2 use ide_db::{
3     helpers::{insert_whitespace_into_node::insert_ws_into, pick_best_token},
4     RootDatabase,
5 };
6 use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T};
7
8 use crate::FilePosition;
9
10 pub struct ExpandedMacro {
11     pub name: String,
12     pub expansion: String,
13 }
14
15 // Feature: Expand Macro Recursively
16 //
17 // Shows the full macro expansion of the macro at current cursor.
18 //
19 // |===
20 // | Editor  | Action Name
21 //
22 // | VS Code | **Rust Analyzer: Expand macro recursively**
23 // |===
24 //
25 // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
26 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
27     let sema = Semantics::new(db);
28     let file = sema.parse(position.file_id);
29
30     let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
31         SyntaxKind::IDENT => 1,
32         _ => 0,
33     })?;
34
35     // due to how Rust Analyzer works internally, we need to special case derive attributes,
36     // otherwise they might not get found, e.g. here with the cursor at $0 `#[attr]` would expand:
37     // ```
38     // #[attr]
39     // #[derive($0Foo)]
40     // struct Bar;
41     // ```
42
43     let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| {
44         let hir_file = sema.hir_file_for(&descended.parent()?);
45         if !hir_file.is_derive_attr_pseudo_expansion(db) {
46             return None;
47         }
48
49         let name = descended.ancestors().filter_map(ast::Path::cast).last()?.to_string();
50         // up map out of the #[derive] expansion
51         let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
52         let attr = token.ancestors().find_map(ast::Attr::cast)?;
53         let expansions = sema.expand_derive_macro(&attr)?;
54         let idx = attr
55             .token_tree()?
56             .token_trees_and_tokens()
57             .filter_map(NodeOrToken::into_token)
58             .take_while(|it| it != &token)
59             .filter(|it| it.kind() == T![,])
60             .count();
61         Some(ExpandedMacro {
62             name,
63             expansion: expansions.get(idx).cloned().map(insert_ws_into)?.to_string(),
64         })
65     });
66
67     if derive.is_some() {
68         return derive;
69     }
70
71     // FIXME: Intermix attribute and bang! expansions
72     // currently we only recursively expand one of the two types
73     let mut expanded = None;
74     let mut name = None;
75     for node in tok.ancestors() {
76         if let Some(item) = ast::Item::cast(node.clone()) {
77             if let Some(def) = sema.resolve_attr_macro_call(&item) {
78                 name = def.name(db).map(|name| name.to_string());
79                 expanded = expand_attr_macro_recur(&sema, &item);
80                 break;
81             }
82         }
83         if let Some(mac) = ast::MacroCall::cast(node) {
84             name = Some(mac.path()?.segment()?.name_ref()?.to_string());
85             expanded = expand_macro_recur(&sema, &mac);
86             break;
87         }
88     }
89
90     // FIXME:
91     // macro expansion may lose all white space information
92     // But we hope someday we can use ra_fmt for that
93     let expansion = insert_ws_into(expanded?).to_string();
94     Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
95 }
96
97 fn expand_macro_recur(
98     sema: &Semantics<RootDatabase>,
99     macro_call: &ast::MacroCall,
100 ) -> Option<SyntaxNode> {
101     let expanded = sema.expand(macro_call)?.clone_for_update();
102     expand(sema, expanded, ast::MacroCall::cast, expand_macro_recur)
103 }
104
105 fn expand_attr_macro_recur(sema: &Semantics<RootDatabase>, item: &ast::Item) -> Option<SyntaxNode> {
106     let expanded = sema.expand_attr_macro(item)?.clone_for_update();
107     expand(sema, expanded, ast::Item::cast, expand_attr_macro_recur)
108 }
109
110 fn expand<T: AstNode>(
111     sema: &Semantics<RootDatabase>,
112     expanded: SyntaxNode,
113     f: impl FnMut(SyntaxNode) -> Option<T>,
114     exp: impl Fn(&Semantics<RootDatabase>, &T) -> Option<SyntaxNode>,
115 ) -> Option<SyntaxNode> {
116     let children = expanded.descendants().filter_map(f);
117     let mut replacements = Vec::new();
118
119     for child in children {
120         if let Some(new_node) = exp(sema, &child) {
121             // check if the whole original syntax is replaced
122             if expanded == *child.syntax() {
123                 return Some(new_node);
124             }
125             replacements.push((child, new_node));
126         }
127     }
128
129     replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
130     Some(expanded)
131 }
132
133 #[cfg(test)]
134 mod tests {
135     use expect_test::{expect, Expect};
136
137     use crate::fixture;
138
139     #[track_caller]
140     fn check(ra_fixture: &str, expect: Expect) {
141         let (analysis, pos) = fixture::position(ra_fixture);
142         let expansion = analysis.expand_macro(pos).unwrap().unwrap();
143         let actual = format!("{}\n{}", expansion.name, expansion.expansion);
144         expect.assert_eq(&actual);
145     }
146
147     #[test]
148     fn macro_expand_as_keyword() {
149         check(
150             r#"
151 macro_rules! bar {
152     ($i:tt) => { $i as _ }
153 }
154 fn main() {
155     let x: u64 = ba$0r!(5i64);
156 }
157 "#,
158             expect![[r#"
159                 bar
160                 5i64 as _"#]],
161         );
162     }
163
164     #[test]
165     fn macro_expand_recursive_expansion() {
166         check(
167             r#"
168 macro_rules! bar {
169     () => { fn  b() {} }
170 }
171 macro_rules! foo {
172     () => { bar!(); }
173 }
174 macro_rules! baz {
175     () => { foo!(); }
176 }
177 f$0oo!();
178 "#,
179             expect![[r#"
180                 foo
181                 fn b(){}
182
183             "#]],
184         );
185     }
186
187     #[test]
188     fn macro_expand_multiple_lines() {
189         check(
190             r#"
191 macro_rules! foo {
192     () => {
193         fn some_thing() -> u32 {
194             let a = 0;
195             a + 10
196         }
197     }
198 }
199 f$0oo!();
200         "#,
201             expect![[r#"
202                 foo
203                 fn some_thing() -> u32 {
204                   let a = 0;
205                   a+10
206                 }
207             "#]],
208         );
209     }
210
211     #[test]
212     fn macro_expand_match_ast() {
213         check(
214             r#"
215 macro_rules! match_ast {
216     (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
217     (match ($node:expr) {
218         $( ast::$ast:ident($it:ident) => $res:block, )*
219         _ => $catch_all:expr $(,)?
220     }) => {{
221         $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
222         { $catch_all }
223     }};
224 }
225
226 fn main() {
227     mat$0ch_ast! {
228         match container {
229             ast::TraitDef(it) => {},
230             ast::ImplDef(it) => {},
231             _ => { continue },
232         }
233     }
234 }
235 "#,
236             expect![[r#"
237        match_ast
238        {
239          if let Some(it) = ast::TraitDef::cast(container.clone()){}
240          else if let Some(it) = ast::ImplDef::cast(container.clone()){}
241          else {
242            {
243              continue
244            }
245          }
246        }"#]],
247         );
248     }
249
250     #[test]
251     fn macro_expand_match_ast_inside_let_statement() {
252         check(
253             r#"
254 macro_rules! match_ast {
255     (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
256     (match ($node:expr) {}) => {{}};
257 }
258
259 fn main() {
260     let p = f(|it| {
261         let res = mat$0ch_ast! { match c {}};
262         Some(res)
263     })?;
264 }
265 "#,
266             expect![[r#"
267                 match_ast
268                 {}
269             "#]],
270         );
271     }
272
273     #[test]
274     fn macro_expand_inner_macro_fail_to_expand() {
275         check(
276             r#"
277 macro_rules! bar {
278     (BAD) => {};
279 }
280 macro_rules! foo {
281     () => {bar!()};
282 }
283
284 fn main() {
285     let res = fo$0o!();
286 }
287 "#,
288             expect![[r#"
289                 foo
290             "#]],
291         );
292     }
293
294     #[test]
295     fn macro_expand_with_dollar_crate() {
296         check(
297             r#"
298 #[macro_export]
299 macro_rules! bar {
300     () => {0};
301 }
302 macro_rules! foo {
303     () => {$crate::bar!()};
304 }
305
306 fn main() {
307     let res = fo$0o!();
308 }
309 "#,
310             expect![[r#"
311                 foo
312                 0 "#]],
313         );
314     }
315
316     #[test]
317     fn macro_expand_with_dyn_absolute_path() {
318         check(
319             r#"
320 macro_rules! foo {
321     () => {fn f<T>(_: &dyn ::std::marker::Copy) {}};
322 }
323
324 fn main() {
325     let res = fo$0o!();
326 }
327 "#,
328             expect![[r#"
329                 foo
330                 fn f<T>(_: &dyn ::std::marker::Copy){}
331             "#]],
332         );
333     }
334
335     #[test]
336     fn macro_expand_derive() {
337         check(
338             r#"
339 //- proc_macros: identity
340 //- minicore: clone, derive
341
342 #[proc_macros::identity]
343 #[derive(C$0lone)]
344 struct Foo {}
345 "#,
346             expect![[r#"
347                 Clone
348                 impl < >core::clone::Clone for Foo< >{}
349
350             "#]],
351         );
352     }
353
354     #[test]
355     fn macro_expand_derive2() {
356         check(
357             r#"
358 //- minicore: copy, clone, derive
359
360 #[derive(Cop$0y)]
361 #[derive(Clone)]
362 struct Foo {}
363 "#,
364             expect![[r#"
365                 Copy
366                 impl < >core::marker::Copy for Foo< >{}
367
368             "#]],
369         );
370     }
371
372     #[test]
373     fn macro_expand_derive_multi() {
374         check(
375             r#"
376 //- minicore: copy, clone, derive
377
378 #[derive(Cop$0y, Clone)]
379 struct Foo {}
380 "#,
381             expect![[r#"
382                 Copy
383                 impl < >core::marker::Copy for Foo< >{}
384
385             "#]],
386         );
387         check(
388             r#"
389 //- minicore: copy, clone, derive
390
391 #[derive(Copy, Cl$0one)]
392 struct Foo {}
393 "#,
394             expect![[r#"
395                 Clone
396                 impl < >core::clone::Clone for Foo< >{}
397
398             "#]],
399         );
400     }
401 }