diff --git a/crates/ide_assists/src/handlers/inline_call.rs b/crates/ide_assists/src/handlers/inline_call.rs index 5b6992b64097..f6840a27b307 100644 --- a/crates/ide_assists/src/handlers/inline_call.rs +++ b/crates/ide_assists/src/handlers/inline_call.rs @@ -14,26 +14,23 @@ use crate::{ // Assist: inline_call // -// Inlines a function or method body. +// Inlines a function or method body creating a `let` statement per parameter unless the parameter +// can be inlined. The parameter will be inlined either if it the supplied argument is a simple local +// or if the parameter is only accessed inside the function body once. // // ``` -// fn align(a: u32, b: u32) -> u32 { -// (a + b - 1) & !(b - 1) -// } -// fn main() { -// let x = align$0(1, 2); +// # //- minicore: option +// fn foo(name: Option<&str>) { +// let name = name.unwrap$0(); // } // ``` // -> // ``` -// fn align(a: u32, b: u32) -> u32 { -// (a + b - 1) & !(b - 1) -// } -// fn main() { -// let x = { -// let b = 2; -// (1 + b - 1) & !(b - 1) -// }; +// fn foo(name: Option<&str>) { +// let name = match name { +// Some(val) => val, +// None => panic!("called `Option::unwrap()` on a `None` value"), +// }; // } // ``` pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -137,7 +134,7 @@ pub(crate) fn inline_( .covering_element(range) .ancestors() .nth(3) - .filter(|it| ast::PathExpr::can_cast(it.kind())), + .and_then(ast::PathExpr::cast), _ => None, }) .collect::>>() @@ -163,7 +160,14 @@ pub(crate) fn inline_( match &*usages { // inline single use parameters [usage] => { - ted::replace(usage, expr.syntax().clone_for_update()); + let expr = if matches!(expr, ast::Expr::ClosureExpr(_)) + && usage.syntax().parent().and_then(ast::Expr::cast).is_some() + { + make::expr_paren(expr) + } else { + expr + }; + ted::replace(usage.syntax(), expr.syntax().clone_for_update()); } // inline parameters whose expression is a simple local reference [_, ..] @@ -173,7 +177,7 @@ pub(crate) fn inline_( ) => { usages.into_iter().for_each(|usage| { - ted::replace(usage, &expr.syntax().clone_for_update()); + ted::replace(usage.syntax(), &expr.syntax().clone_for_update()); }); } // cant inline, emit a let statement @@ -540,6 +544,56 @@ impl Foo { }; } } +"#, + ); + } + + #[test] + fn wraps_closure_in_paren() { + check_assist( + inline_call, + r#" +fn foo(x: fn()) { + x(); +} + +fn main() { + foo$0(|| {}) +} +"#, + r#" +fn foo(x: fn()) { + x(); +} + +fn main() { + { + (|| {})(); + } +} +"#, + ); + check_assist( + inline_call, + r#" +fn foo(x: fn()) { + x(); +} + +fn main() { + foo$0(main) +} +"#, + r#" +fn foo(x: fn()) { + x(); +} + +fn main() { + { + main(); + } +} "#, ); } diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index c24c38034501..e35e68b40dca 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -923,22 +923,17 @@ fn doctest_inline_call() { check_doc_test( "inline_call", r#####" -fn align(a: u32, b: u32) -> u32 { - (a + b - 1) & !(b - 1) -} -fn main() { - let x = align$0(1, 2); +//- minicore: option +fn foo(name: Option<&str>) { + let name = name.unwrap$0(); } "#####, r#####" -fn align(a: u32, b: u32) -> u32 { - (a + b - 1) & !(b - 1) -} -fn main() { - let x = { - let b = 2; - (1 + b - 1) & !(b - 1) - }; +fn foo(name: Option<&str>) { + let name = match name { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + }; } "#####, ) diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 4e12fe150797..e16021c82ffc 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs @@ -1,4 +1,7 @@ //! Handle syntactic aspects of inserting a new `use`. +#[cfg(test)] +mod tests; + use std::cmp::Ordering; use hir::Semantics; @@ -378,5 +381,3 @@ fn is_inner_comment(token: SyntaxToken) -> bool { ast::Comment::cast(token).and_then(|comment| comment.kind().doc) == Some(ast::CommentPlacement::Inner) } -#[cfg(test)] -mod tests; diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 2e0153a180d5..c5420f467965 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs @@ -14,7 +14,7 @@ fn foo() {$0} r#" #[cfg(test)] fn foo() { -use bar::Bar; + use bar::Bar; } "#, ImportGranularity::Crate, @@ -32,7 +32,7 @@ const FOO: Bar = {$0}; r#" #[cfg(test)] const FOO: Bar = { -use bar::Bar; + use bar::Bar; }; "#, ImportGranularity::Crate, diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 3c05b4881572..2396408270ef 100644 --- a/crates/syntax/src/ted.rs +++ b/crates/syntax/src/ted.rs @@ -161,6 +161,14 @@ fn ws_before(position: &Position, new: &SyntaxElement) -> Option { } } + if prev.kind() == T!['{'] && ast::Stmt::can_cast(new.kind()) { + if let Some(block_expr) = prev.parent().and_then(ast::BlockExpr::cast) { + let mut indent = IndentLevel::from_element(&block_expr.syntax().clone().into()); + indent.0 += 1; + return Some(make::tokens::whitespace(&format!("\n{}", indent))); + } + } + ws_between(prev, new) } fn ws_after(position: &Position, new: &SyntaxElement) -> Option { @@ -187,12 +195,6 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option Option { + pub const fn unwrap(self) -> T { + match self { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + } + } + } } // endregion:option