Skip to content
Open
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
98 changes: 93 additions & 5 deletions crates/ide-assists/src/handlers/inline_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ide_db::{
};
use itertools::{Itertools, izip};
use syntax::{
AstNode, NodeOrToken, SyntaxKind, TextRange,
AstNode, NodeOrToken, SyntaxKind, T, TextRange,
ast::{
self, HasArgList, HasGenericArgs, Pat, PathExpr,
edit::{AstNodeEdit, IndentLevel},
Expand Down Expand Up @@ -345,6 +345,7 @@ fn inline<'db>(
file_editor: &SyntaxEditor,
) -> ast::Expr {
let make = file_editor.make();
// Do not use original range as the body may be generated by a macro without mapping
let file_id = sema.hir_file_for(fn_body.syntax());

// Capture before `with_ast_node` re-roots and loses the source-relative position.
Expand All @@ -353,13 +354,15 @@ fn inline<'db>(
let (editor, body) = SyntaxEditor::with_ast_node(fn_body);

let usages_for_locals = |local| {
let body_root = fn_body.syntax().tree_top();
Definition::Local(local)
.usages(sema)
.all()
.references
.remove(&function_def_file_id)
.unwrap_or_default()
.into_iter()
.filter(move |it| it.name.syntax().tree_top() == body_root)
};
let param_use_nodes: Vec<Vec<_>> = params
.iter()
Expand Down Expand Up @@ -471,6 +474,27 @@ fn inline<'db>(
for usage in &self_token_usages {
editor.replace(usage.clone(), this_token.clone());
}
// Due to the difficulty in handling the parameters referenced by macro calls
// in macro generation functions, so add a syntax-based fallback
let mut depth = 0u32;
let not_replaced = |it: &syntax::SyntaxElement| {
self_token_usages
.iter()
.all(|usage| !usage.text_range().contains_range(it.text_range()))
};
let is_item_like = |kind| ast::Item::can_cast(kind) && !ast::MacroCall::can_cast(kind);
for event in body.syntax().preorder_with_tokens() {
match event {
syntax::WalkEvent::Enter(it) if is_item_like(it.kind()) => depth += 1,
syntax::WalkEvent::Leave(it) if is_item_like(it.kind()) => depth -= 1,
syntax::WalkEvent::Enter(it)
if it.kind() == T![self] && depth == 0 && not_replaced(&it) =>
{
editor.replace(it, this_token.clone());
}
_ => (),
}
}
};

// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
Expand Down Expand Up @@ -611,10 +635,15 @@ fn inline<'db>(
body = ast::BlockExpr::cast(body_prettified).unwrap();
}

fn reindent<T: AstNodeEdit>(value: T) -> T {
value.reset_indent().indent(1.into())
}
let is_async_fn = function.is_async(sema.db);
if is_async_fn {
cov_mark::hit!(inline_call_async_fn);
body = make.async_move_block_expr(body.statements(), body.tail_expr());
body = make
.async_move_block_expr(body.statements().map(reindent), body.tail_expr().map(reindent));
original_body_indent = IndentLevel(0);

// Arguments should be evaluated outside the async block, and then moved into it.
if !let_stmts.is_empty() {
Expand All @@ -624,8 +653,9 @@ fn inline<'db>(
}
} else if !let_stmts.is_empty() {
// Prepend let statements to the body's existing statements
let stmts: Vec<ast::Stmt> = let_stmts.into_iter().chain(body.statements()).collect();
body = make.block_expr(stmts, body.tail_expr());
let stmts: Vec<ast::Stmt> =
let_stmts.into_iter().chain(body.statements().map(reindent)).collect();
body = make.block_expr(stmts, body.tail_expr().map(reindent));
original_body_indent = IndentLevel(0);
}

Expand Down Expand Up @@ -1917,6 +1947,64 @@ pub fn main() {

#[test]
fn inline_call_with_reference_in_macros() {
check_assist(
inline_call,
r#"
macro_rules! identity { ($($t:tt)*) => { $($t)* }; }
struct Foo;
impl Foo {
fn foo(self, r1: u32, r2: u32, (complex,): (u32,)) {
identity! {
let _ = self;
let _ = r1;
let _ = (r2, r2);
let _ = complex;
}
impl Foo {
fn nested(self) { _ = self; }
}
}
}
fn bar() {
Foo.$0foo(1, 2, (3,));
}
"#,
r#"
macro_rules! identity { ($($t:tt)*) => { $($t)* }; }
struct Foo;
impl Foo {
fn foo(self, r1: u32, r2: u32, (complex,): (u32,)) {
identity! {
let _ = self;
let _ = r1;
let _ = (r2, r2);
let _ = complex;
}
impl Foo {
fn nested(self) { _ = self; }
}
}
}
fn bar() {
{
let this = Foo;
let r1 = 1;
let r2 = 2;
let (complex,) = (3,);
identity! {
let _ = this;
let _ = r1;
let _ = (r2, r2);
let _ = complex;
}
impl Foo {
fn nested(self) { _ = self; }
}
};
}
"#,
);

check_assist(
inline_call,
r#"
Expand Down Expand Up @@ -1955,7 +2043,7 @@ fn _hash2(self_: &u64, state: &mut u64) {
};
}
"#,
)
);
}

#[test]
Expand Down