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