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