summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-assists/src/handlers/inline_macro.rs242
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs33
3 files changed, 277 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs
new file mode 100644
index 00000000000..d669826aa7a
--- /dev/null
+++ b/crates/ide-assists/src/handlers/inline_macro.rs
@@ -0,0 +1,242 @@
+use syntax::ast::{self, AstNode};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: inline_macro
+//
+// Takes a macro and inlines it one step.
+//
+// ```
+// macro_rules! num {
+// (+$($t:tt)+) => (1 + num!($($t )+));
+// (-$($t:tt)+) => (-1 + num!($($t )+));
+// (+) => (1);
+// (-) => (-1);
+// }
+//
+// fn main() {
+// let number = num$0!(+ + + - + +);
+// println!("{number}");
+// }
+// ```
+// ->
+// ```
+// macro_rules! num {
+// (+$($t:tt)+) => (1 + num!($($t )+));
+// (-$($t:tt)+) => (-1 + num!($($t )+));
+// (+) => (1);
+// (-) => (-1);
+// }
+//
+// fn main() {
+// let number = 1+num!(+ + - + +);
+// println!("{number}");
+// }
+// ```
+pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let tok = ctx.token_at_offset().right_biased()?;
+
+ let mut anc = tok.parent_ancestors();
+ let (_name, expanded, unexpanded) = loop {
+ let node = anc.next()?;
+ if let Some(mac) = ast::MacroCall::cast(node.clone()) {
+ break (
+ mac.path()?.segment()?.name_ref()?.to_string(),
+ ctx.sema.expand(&mac)?.clone_for_update(),
+ node,
+ );
+ }
+ };
+
+ acc.add(
+ AssistId("inline_macro", AssistKind::RefactorRewrite),
+ format!("Inline macro"),
+ unexpanded.text_range(),
+ |builder| builder.replace(unexpanded.text_range(), expanded.to_string()),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+
+ macro_rules! simple_macro {
+ () => {
+ r#"
+macro_rules! foo {
+ (foo) => (true);
+ () => (false);
+}
+"#
+ };
+ }
+ macro_rules! double_macro {
+ () => {
+ r#"
+macro_rules! bar {
+ (bar) => (true);
+ ($($tt:tt)?) => (false);
+}
+macro_rules! foo {
+ (foo) => (true);
+ (bar) => (bar!(bar));
+ ($($tt:tt)?) => (bar!($($tt)?));
+}
+"#
+ };
+ }
+
+ macro_rules! complex_macro {
+ () => {
+ r#"
+macro_rules! num {
+ (+$($t:tt)+) => (1 + num!($($t )+));
+ (-$($t:tt)+) => (-1 + num!($($t )+));
+ (+) => (1);
+ (-) => (-1);
+}
+"#
+ };
+ }
+ #[test]
+ fn inline_macro_target() {
+ check_assist_target(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let a = foo$0!(foo); }"#),
+ "foo!(foo)",
+ );
+ }
+
+ #[test]
+ fn inline_macro_target_start() {
+ check_assist_target(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let a = $0foo!(foo); }"#),
+ "foo!(foo)",
+ );
+ }
+
+ #[test]
+ fn inline_macro_target_end() {
+ check_assist_target(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let a = foo!(foo$0); }"#),
+ "foo!(foo)",
+ );
+ }
+
+ #[test]
+ fn inline_macro_simple_case1() {
+ check_assist(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let result = foo$0!(foo); }"#),
+ concat!(simple_macro!(), r#"fn f() { let result = true; }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_simple_case2() {
+ check_assist(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let result = foo$0!(); }"#),
+ concat!(simple_macro!(), r#"fn f() { let result = false; }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_simple_not_applicable() {
+ check_assist_not_applicable(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let result$0 = foo!(foo); }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_simple_not_applicable_broken_macro() {
+ // FIXME: This is a bug. The macro should not expand, but it's
+ // the same behaviour as the "Expand Macro Recursively" commmand
+ // so it's presumably OK for the time being.
+ check_assist(
+ inline_macro,
+ concat!(simple_macro!(), r#"fn f() { let result = foo$0!(asdfasdf); }"#),
+ concat!(simple_macro!(), r#"fn f() { let result = true; }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_double_case1() {
+ check_assist(
+ inline_macro,
+ concat!(double_macro!(), r#"fn f() { let result = foo$0!(bar); }"#),
+ concat!(double_macro!(), r#"fn f() { let result = bar!(bar); }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_double_case2() {
+ check_assist(
+ inline_macro,
+ concat!(double_macro!(), r#"fn f() { let result = foo$0!(asdf); }"#),
+ concat!(double_macro!(), r#"fn f() { let result = bar!(asdf); }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_complex_case1() {
+ check_assist(
+ inline_macro,
+ concat!(complex_macro!(), r#"fn f() { let result = num!(+ +$0 + - +); }"#),
+ concat!(complex_macro!(), r#"fn f() { let result = 1+num!(+ + - +); }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_complex_case2() {
+ check_assist(
+ inline_macro,
+ concat!(complex_macro!(), r#"fn f() { let result = n$0um!(- + + - +); }"#),
+ concat!(complex_macro!(), r#"fn f() { let result = -1+num!(+ + - +); }"#),
+ );
+ }
+
+ #[test]
+ fn inline_macro_recursive_macro() {
+ check_assist(
+ inline_macro,
+ r#"
+macro_rules! foo {
+ () => {foo!()}
+}
+fn f() { let result = foo$0!(); }
+"#,
+ r#"
+macro_rules! foo {
+ () => {foo!()}
+}
+fn f() { let result = foo!(); }
+"#,
+ );
+ }
+
+ #[test]
+ fn inline_macro_unknown_macro() {
+ check_assist_not_applicable(
+ inline_macro,
+ r#"
+fn f() { let result = foo$0!(); }
+"#,
+ );
+ }
+
+ #[test]
+ fn inline_macro_function_call_not_applicable() {
+ check_assist_not_applicable(
+ inline_macro,
+ r#"
+fn f() { let result = foo$0(); }
+"#,
+ );
+ }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index b12f99cc532..1e6d755faed 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -159,6 +159,7 @@ mod handlers {
mod add_return_type;
mod inline_call;
mod inline_local_variable;
+ mod inline_macro;
mod inline_type_alias;
mod introduce_named_lifetime;
mod invert_if;
@@ -255,6 +256,7 @@ mod handlers {
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
+ inline_macro::inline_macro,
introduce_named_generic::introduce_named_generic,
introduce_named_lifetime::introduce_named_lifetime,
invert_if::invert_if,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index d797f077672..666b794c01a 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1439,6 +1439,39 @@ fn main() {
}
#[test]
+fn doctest_inline_macro() {
+ check_doc_test(
+ "inline_macro",
+ r#####"
+macro_rules! num {
+ (+$($t:tt)+) => (1 + num!($($t )+));
+ (-$($t:tt)+) => (-1 + num!($($t )+));
+ (+) => (1);
+ (-) => (-1);
+}
+
+fn main() {
+ let number = num$0!(+ + + - + +);
+ println!("{number}");
+}
+"#####,
+ r#####"
+macro_rules! num {
+ (+$($t:tt)+) => (1 + num!($($t )+));
+ (-$($t:tt)+) => (-1 + num!($($t )+));
+ (+) => (1);
+ (-) => (-1);
+}
+
+fn main() {
+ let number = 1+num!(+ + - + +);
+ println!("{number}");
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_inline_type_alias() {
check_doc_test(
"inline_type_alias",