1use hir::db::ExpandDatabase;
2use ide_db::syntax_helpers::prettify_macro_expansion;
3use syntax::ast::{self, AstNode, edit::AstNodeEdit};
4
5use crate::{AssistContext, AssistId, Assists};
6
7pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
39 let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
40 let macro_call = ctx.sema.to_def(&unexpanded)?;
41 let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into();
42 let text_range = unexpanded.syntax().text_range();
43
44 acc.add(
45 AssistId::refactor_inline("inline_macro"),
46 "Inline macro".to_owned(),
47 text_range,
48 |builder| {
49 let editor = builder.make_editor(unexpanded.syntax());
50 let expanded = ctx.sema.parse_or_expand(macro_call.into());
51 let span_map = ctx.sema.db.expansion_span_map(macro_call);
52 let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id);
55 let expanded = ast::edit::indent(&expanded, unexpanded.indent_level());
56 editor.replace(unexpanded.syntax(), expanded);
57 builder.add_file_edits(ctx.vfs_file_id(), editor);
58 },
59 )
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
67
68 macro_rules! simple_macro {
69 () => {
70 r#"
71macro_rules! foo {
72 (foo) => (true);
73 () => (false);
74}
75"#
76 };
77 }
78 macro_rules! double_macro {
79 () => {
80 r#"
81macro_rules! bar {
82 (bar) => (true);
83 ($($tt:tt)?) => (false);
84}
85macro_rules! foo {
86 (foo) => (true);
87 (bar) => (bar!(bar));
88 ($($tt:tt)?) => (bar!($($tt)?));
89}
90"#
91 };
92 }
93
94 macro_rules! complex_macro {
95 () => {
96 r#"
97macro_rules! num {
98 (+$($t:tt)+) => (1 + num!($($t )+));
99 (-$($t:tt)+) => (-1 + num!($($t )+));
100 (+) => (1);
101 (-) => (-1);
102}
103"#
104 };
105 }
106 #[test]
107 fn inline_macro_target() {
108 check_assist_target(
109 inline_macro,
110 concat!(simple_macro!(), r#"fn f() { let a = foo$0!(foo); }"#),
111 "foo!(foo)",
112 );
113 }
114
115 #[test]
116 fn inline_macro_target_start() {
117 check_assist_target(
118 inline_macro,
119 concat!(simple_macro!(), r#"fn f() { let a = $0foo!(foo); }"#),
120 "foo!(foo)",
121 );
122 }
123
124 #[test]
125 fn inline_macro_target_end() {
126 check_assist_target(
127 inline_macro,
128 concat!(simple_macro!(), r#"fn f() { let a = foo!(foo$0); }"#),
129 "foo!(foo)",
130 );
131 }
132
133 #[test]
134 fn inline_macro_simple_case1() {
135 check_assist(
136 inline_macro,
137 concat!(simple_macro!(), r#"fn f() { let result = foo$0!(foo); }"#),
138 concat!(simple_macro!(), r#"fn f() { let result = true; }"#),
139 );
140 }
141
142 #[test]
143 fn inline_macro_simple_case2() {
144 check_assist(
145 inline_macro,
146 concat!(simple_macro!(), r#"fn f() { let result = foo$0!(); }"#),
147 concat!(simple_macro!(), r#"fn f() { let result = false; }"#),
148 );
149 }
150
151 #[test]
152 fn inline_macro_simple_not_applicable() {
153 check_assist_not_applicable(
154 inline_macro,
155 concat!(simple_macro!(), r#"fn f() { let result$0 = foo!(foo); }"#),
156 );
157 }
158
159 #[test]
160 fn inline_macro_simple_not_applicable_broken_macro() {
161 check_assist(
165 inline_macro,
166 concat!(simple_macro!(), r#"fn f() { let result = foo$0!(asdfasdf); }"#),
167 concat!(simple_macro!(), r#"fn f() { let result = true; }"#),
168 );
169 }
170
171 #[test]
172 fn inline_macro_double_case1() {
173 check_assist(
174 inline_macro,
175 concat!(double_macro!(), r#"fn f() { let result = foo$0!(bar); }"#),
176 concat!(double_macro!(), r#"fn f() { let result = bar!(bar); }"#),
177 );
178 }
179
180 #[test]
181 fn inline_macro_double_case2() {
182 check_assist(
183 inline_macro,
184 concat!(double_macro!(), r#"fn f() { let result = foo$0!(asdf); }"#),
185 concat!(double_macro!(), r#"fn f() { let result = bar!(asdf); }"#),
186 );
187 }
188
189 #[test]
190 fn inline_macro_complex_case1() {
191 check_assist(
192 inline_macro,
193 concat!(complex_macro!(), r#"fn f() { let result = num!(+ +$0 + - +); }"#),
194 concat!(complex_macro!(), r#"fn f() { let result = 1+num!(+ + - +); }"#),
195 );
196 }
197
198 #[test]
199 fn inline_macro_complex_case2() {
200 check_assist(
201 inline_macro,
202 concat!(complex_macro!(), r#"fn f() { let result = n$0um!(- + + - +); }"#),
203 concat!(complex_macro!(), r#"fn f() { let result = -1+num!(+ + - +); }"#),
204 );
205 }
206
207 #[test]
208 fn inline_macro_recursive_macro() {
209 check_assist(
210 inline_macro,
211 r#"
212macro_rules! foo {
213 ($t:tt) => {foo!(1)}
214}
215fn f() { let result = foo$0!(0); }
216"#,
217 r#"
218macro_rules! foo {
219 ($t:tt) => {foo!(1)}
220}
221fn f() { let result = foo!(1); }
222"#,
223 );
224 }
225
226 #[test]
227 fn inline_macro_unknown_macro() {
228 check_assist_not_applicable(
229 inline_macro,
230 r#"
231fn f() { let result = foo$0!(); }
232"#,
233 );
234 }
235
236 #[test]
237 fn inline_macro_function_call_not_applicable() {
238 check_assist_not_applicable(
239 inline_macro,
240 r#"
241fn f() { let result = foo$0(); }
242"#,
243 );
244 }
245
246 #[test]
247 fn inline_macro_with_whitespace() {
248 check_assist(
249 inline_macro,
250 r#"
251macro_rules! whitespace {
252 () => {
253 if true {}
254 };
255}
256fn f() { whitespace$0!(); }
257"#,
258 r#"
259macro_rules! whitespace {
260 () => {
261 if true {}
262 };
263}
264fn f() { if true {}; }
265"#,
266 )
267 }
268
269 #[test]
270 fn whitespace_between_text_and_pound() {
271 check_assist(
272 inline_macro,
273 r#"
274macro_rules! foo {
275 () => {
276 cfg_if! {
277 if #[cfg(test)] {
278 1;
279 } else {
280 1;
281 }
282 }
283 }
284}
285fn main() {
286 $0foo!();
287}
288"#,
289 r#"
290macro_rules! foo {
291 () => {
292 cfg_if! {
293 if #[cfg(test)] {
294 1;
295 } else {
296 1;
297 }
298 }
299 }
300}
301fn main() {
302 cfg_if!{
303 if #[cfg(test)]{
304 1;
305 }else {
306 1;
307 }
308 };
309}
310"#,
311 );
312 }
313
314 #[test]
315 fn dollar_crate() {
316 check_assist(
317 inline_macro,
318 r#"
319pub struct Foo;
320#[macro_export]
321macro_rules! m {
322 () => { $crate::Foo };
323}
324fn bar() {
325 m$0!();
326}
327"#,
328 r#"
329pub struct Foo;
330#[macro_export]
331macro_rules! m {
332 () => { $crate::Foo };
333}
334fn bar() {
335 crate::Foo;
336}
337"#,
338 );
339 check_assist(
340 inline_macro,
341 r#"
342//- /a.rs crate:a
343pub struct Foo;
344#[macro_export]
345macro_rules! m {
346 () => { $crate::Foo };
347}
348//- /b.rs crate:b deps:a
349fn bar() {
350 a::m$0!();
351}
352"#,
353 r#"
354fn bar() {
355 a::Foo;
356}
357"#,
358 );
359 check_assist(
360 inline_macro,
361 r#"
362//- /a.rs crate:a
363pub struct Foo;
364#[macro_export]
365macro_rules! m {
366 () => { $crate::Foo };
367}
368//- /b.rs crate:b deps:a
369pub use a::m;
370//- /c.rs crate:c deps:b
371fn bar() {
372 b::m$0!();
373}
374"#,
375 r#"
376fn bar() {
377 a::Foo;
378}
379"#,
380 );
381 }
382}