Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions crates/ide-assists/src/handlers/inline_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
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 unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
let expanded = ctx.sema.expand(&unexpanded)?.clone_for_update();

let text_range = unexpanded.syntax().text_range();

acc.add(
AssistId("inline_macro", AssistKind::RefactorRewrite),
format!("Inline macro"),
text_range,
|builder| builder.replace(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(); }
"#,
);
}
}
2 changes: 2 additions & 0 deletions crates/ide-assists/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
33 changes: 33 additions & 0 deletions crates/ide-assists/src/tests/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,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(
Expand Down