]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/expand_macro.rs
Merge #10014
[rust.git] / crates / ide / src / expand_macro.rs
1 use std::iter;
2
3 use hir::Semantics;
4 use ide_db::{helpers::pick_best_token, RootDatabase};
5 use itertools::Itertools;
6 use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, 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     let mut expanded = None;
35     let mut name = None;
36     for node in tok.ancestors() {
37         if let Some(attr) = ast::Attr::cast(node.clone()) {
38             if let Some((path, tt)) = attr.as_simple_call() {
39                 if path == "derive" {
40                     let mut tt = tt.syntax().children_with_tokens().skip(1).join("");
41                     tt.pop();
42                     name = Some(tt);
43                     expanded = sema.expand_derive_macro(&attr);
44                     break;
45                 }
46             }
47         }
48
49         if let Some(item) = ast::Item::cast(node.clone()) {
50             if let Some(def) = sema.resolve_attr_macro_call(&item) {
51                 name = def.name(db).map(|name| name.to_string());
52                 expanded = expand_attr_macro_recur(&sema, &item);
53                 break;
54             }
55         }
56
57         if let Some(mac) = ast::MacroCall::cast(node) {
58             name = Some(mac.path()?.segment()?.name_ref()?.to_string());
59             expanded = expand_macro_recur(&sema, &mac);
60             break;
61         }
62     }
63
64     // FIXME:
65     // macro expansion may lose all white space information
66     // But we hope someday we can use ra_fmt for that
67     let expansion = insert_whitespaces(expanded?);
68     Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
69 }
70
71 fn expand_macro_recur(
72     sema: &Semantics<RootDatabase>,
73     macro_call: &ast::MacroCall,
74 ) -> Option<SyntaxNode> {
75     let expanded = sema.expand(macro_call)?.clone_for_update();
76     expand(sema, expanded, ast::MacroCall::cast, expand_macro_recur)
77 }
78
79 fn expand_attr_macro_recur(sema: &Semantics<RootDatabase>, item: &ast::Item) -> Option<SyntaxNode> {
80     let expanded = sema.expand_attr_macro(item)?.clone_for_update();
81     expand(sema, expanded, ast::Item::cast, expand_attr_macro_recur)
82 }
83
84 fn expand<T: AstNode>(
85     sema: &Semantics<RootDatabase>,
86     expanded: SyntaxNode,
87     f: impl FnMut(SyntaxNode) -> Option<T>,
88     exp: impl Fn(&Semantics<RootDatabase>, &T) -> Option<SyntaxNode>,
89 ) -> Option<SyntaxNode> {
90     let children = expanded.descendants().filter_map(f);
91     let mut replacements = Vec::new();
92
93     for child in children {
94         if let Some(new_node) = exp(sema, &child) {
95             // check if the whole original syntax is replaced
96             if expanded == *child.syntax() {
97                 return Some(new_node);
98             }
99             replacements.push((child, new_node));
100         }
101     }
102
103     replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
104     Some(expanded)
105 }
106
107 // FIXME: It would also be cool to share logic here and in the mbe tests,
108 // which are pretty unreadable at the moment.
109 fn insert_whitespaces(syn: SyntaxNode) -> String {
110     let mut res = String::new();
111     let mut token_iter = syn
112         .preorder_with_tokens()
113         .filter_map(|event| {
114             if let WalkEvent::Enter(NodeOrToken::Token(token)) = event {
115                 Some(token)
116             } else {
117                 None
118             }
119         })
120         .peekable();
121
122     let mut indent = 0;
123     let mut last: Option<SyntaxKind> = None;
124
125     while let Some(token) = token_iter.next() {
126         let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
127             token_iter.peek().map(|it| f(it.kind())).unwrap_or(default)
128         };
129         let is_last =
130             |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
131
132         match token.kind() {
133             k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
134                 res.push_str(token.text());
135                 res.push(' ');
136             }
137             L_CURLY if is_next(|it| it != R_CURLY, true) => {
138                 indent += 1;
139                 if is_last(is_text, false) {
140                     res.push(' ');
141                 }
142                 res.push_str("{\n");
143                 res.extend(iter::repeat(" ").take(2 * indent));
144             }
145             R_CURLY if is_last(|it| it != L_CURLY, true) => {
146                 indent = indent.saturating_sub(1);
147                 res.push('\n');
148                 res.extend(iter::repeat(" ").take(2 * indent));
149                 res.push_str("}");
150             }
151             R_CURLY => {
152                 res.push_str("}\n");
153                 res.extend(iter::repeat(" ").take(2 * indent));
154             }
155             LIFETIME_IDENT if is_next(|it| it == IDENT, true) => {
156                 res.push_str(token.text());
157                 res.push(' ');
158             }
159             T![;] => {
160                 res.push_str(";\n");
161                 res.extend(iter::repeat(" ").take(2 * indent));
162             }
163             T![->] => res.push_str(" -> "),
164             T![=] => res.push_str(" = "),
165             T![=>] => res.push_str(" => "),
166             _ => res.push_str(token.text()),
167         }
168
169         last = Some(token.kind());
170     }
171
172     return res;
173
174     fn is_text(k: SyntaxKind) -> bool {
175         k.is_keyword() || k.is_literal() || k == IDENT
176     }
177 }
178
179 #[cfg(test)]
180 mod tests {
181     use expect_test::{expect, Expect};
182
183     use crate::fixture;
184
185     fn check(ra_fixture: &str, expect: Expect) {
186         let (analysis, pos) = fixture::position(ra_fixture);
187         let expansion = analysis.expand_macro(pos).unwrap().unwrap();
188         let actual = format!("{}\n{}", expansion.name, expansion.expansion);
189         expect.assert_eq(&actual);
190     }
191
192     #[test]
193     fn macro_expand_recursive_expansion() {
194         check(
195             r#"
196 macro_rules! bar {
197     () => { fn  b() {} }
198 }
199 macro_rules! foo {
200     () => { bar!(); }
201 }
202 macro_rules! baz {
203     () => { foo!(); }
204 }
205 f$0oo!();
206 "#,
207             expect![[r#"
208                 foo
209                 fn b(){}
210             "#]],
211         );
212     }
213
214     #[test]
215     fn macro_expand_multiple_lines() {
216         check(
217             r#"
218 macro_rules! foo {
219     () => {
220         fn some_thing() -> u32 {
221             let a = 0;
222             a + 10
223         }
224     }
225 }
226 f$0oo!();
227         "#,
228             expect![[r#"
229             foo
230             fn some_thing() -> u32 {
231               let a = 0;
232               a+10
233             }"#]],
234         );
235     }
236
237     #[test]
238     fn macro_expand_match_ast() {
239         check(
240             r#"
241 macro_rules! match_ast {
242     (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
243     (match ($node:expr) {
244         $( ast::$ast:ident($it:ident) => $res:block, )*
245         _ => $catch_all:expr $(,)?
246     }) => {{
247         $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
248         { $catch_all }
249     }};
250 }
251
252 fn main() {
253     mat$0ch_ast! {
254         match container {
255             ast::TraitDef(it) => {},
256             ast::ImplDef(it) => {},
257             _ => { continue },
258         }
259     }
260 }
261 "#,
262             expect![[r#"
263        match_ast
264        {
265          if let Some(it) = ast::TraitDef::cast(container.clone()){}
266          else if let Some(it) = ast::ImplDef::cast(container.clone()){}
267          else {
268            {
269              continue
270            }
271          }
272        }"#]],
273         );
274     }
275
276     #[test]
277     fn macro_expand_match_ast_inside_let_statement() {
278         check(
279             r#"
280 macro_rules! match_ast {
281     (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
282     (match ($node:expr) {}) => {{}};
283 }
284
285 fn main() {
286     let p = f(|it| {
287         let res = mat$0ch_ast! { match c {}};
288         Some(res)
289     })?;
290 }
291 "#,
292             expect![[r#"
293                 match_ast
294                 {}
295             "#]],
296         );
297     }
298
299     #[test]
300     fn macro_expand_inner_macro_fail_to_expand() {
301         check(
302             r#"
303 macro_rules! bar {
304     (BAD) => {};
305 }
306 macro_rules! foo {
307     () => {bar!()};
308 }
309
310 fn main() {
311     let res = fo$0o!();
312 }
313 "#,
314             expect![[r#"
315                 foo
316             "#]],
317         );
318     }
319
320     #[test]
321     fn macro_expand_with_dollar_crate() {
322         check(
323             r#"
324 #[macro_export]
325 macro_rules! bar {
326     () => {0};
327 }
328 macro_rules! foo {
329     () => {$crate::bar!()};
330 }
331
332 fn main() {
333     let res = fo$0o!();
334 }
335 "#,
336             expect![[r#"
337                 foo
338                 0 "#]],
339         );
340     }
341
342     #[test]
343     fn macro_expand_derive() {
344         check(
345             r#"
346
347 #[rustc_builtin_macro]
348 pub macro Clone {}
349
350 #[derive(C$0lone)]
351 struct Foo {}
352 "#,
353             expect![[r#"
354                 Clone
355                 impl< >crate::clone::Clone for Foo< >{}
356             "#]],
357         );
358     }
359 }