diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 621ac9223abd..78c4d3007e56 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -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}, @@ -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. @@ -353,6 +354,7 @@ 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() @@ -360,6 +362,7 @@ fn inline<'db>( .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> = params .iter() @@ -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. @@ -611,10 +635,15 @@ fn inline<'db>( body = ast::BlockExpr::cast(body_prettified).unwrap(); } + fn reindent(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() { @@ -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 = let_stmts.into_iter().chain(body.statements()).collect(); - body = make.block_expr(stmts, body.tail_expr()); + let stmts: Vec = + 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); } @@ -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#" @@ -1955,7 +2043,7 @@ fn _hash2(self_: &u64, state: &mut u64) { }; } "#, - ) + ); } #[test]