Skip to content

Commit d20efdc

Browse files
committed
Fix assist and -> and_then parameter
- And fix fallback parentheses Example --- ```rust fn foo() { let foo = Some("foo"); return foo.and$0(Some("bar")); } ``` **Before this PR** ```rust fn foo() { let foo = Some("foo"); return foo.and_then(|| Some("bar")); } ``` **After this PR** ```rust fn foo() { let foo = Some("foo"); return foo.and_then(|it| Some("bar")); } ``` --- ```rust struct Func { f: fn() -> i32 } fn foo() { let foo = true; let func = Func { f: || 2 }; let x = foo.then$0(func.f); } ``` **Before this PR** ```text request handler panicked: Failed to make ast node `syntax::ast::generated::nodes::CallExpr` from text const C: () = func.f(); ``` **After this PR** ```rust struct Func { f: fn() -> i32 } fn foo() { let foo = true; let func = Func { f: || 2 }; let x = foo.then_some((func.f)()); } ```
1 parent e8ac252 commit d20efdc

File tree

1 file changed

+83
-10
lines changed

1 file changed

+83
-10
lines changed

crates/ide-assists/src/handlers/replace_method_eager_lazy.rs

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use ide_db::assists::AssistId;
1+
use hir::Semantics;
2+
use ide_db::{RootDatabase, assists::AssistId, defs::Definition};
23
use syntax::{
34
AstNode,
45
ast::{self, Expr, HasArgList, make},
@@ -60,8 +61,8 @@ pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_
6061
format!("Replace {method_name} with {method_name_lazy}"),
6162
call.syntax().text_range(),
6263
|builder| {
64+
let closured = into_closure(&last_arg, &method_name_lazy);
6365
builder.replace(method_name.syntax().text_range(), method_name_lazy);
64-
let closured = into_closure(&last_arg);
6566
builder.replace_ast(last_arg, closured);
6667
},
6768
)
@@ -79,15 +80,19 @@ fn lazy_method_name(name: &str) -> String {
7980
}
8081
}
8182

82-
fn into_closure(param: &Expr) -> Expr {
83+
fn into_closure(param: &Expr, name_lazy: &str) -> Expr {
8384
(|| {
8485
if let ast::Expr::CallExpr(call) = param {
8586
if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None }
8687
} else {
8788
None
8889
}
8990
})()
90-
.unwrap_or_else(|| make::expr_closure(None, param.clone()).into())
91+
.unwrap_or_else(|| {
92+
let pats = (name_lazy == "and_then")
93+
.then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into()));
94+
make::expr_closure(pats, param.clone()).into()
95+
})
9196
}
9297

9398
// Assist: replace_with_eager_method
@@ -146,21 +151,39 @@ pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'
146151
call.syntax().text_range(),
147152
|builder| {
148153
builder.replace(method_name.syntax().text_range(), method_name_eager);
149-
let called = into_call(&last_arg);
154+
let called = into_call(&last_arg, &ctx.sema);
150155
builder.replace_ast(last_arg, called);
151156
},
152157
)
153158
}
154159

155-
fn into_call(param: &Expr) -> Expr {
160+
fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr {
156161
(|| {
157162
if let ast::Expr::ClosureExpr(closure) = param {
158-
if closure.param_list()?.params().count() == 0 { Some(closure.body()?) } else { None }
163+
let mut params = closure.param_list()?.params();
164+
match params.next() {
165+
Some(_) if params.next().is_none() => {
166+
let params = sema.resolve_expr_as_callable(param)?.params();
167+
let used_param = Definition::Local(params.first()?.as_local(sema.db)?)
168+
.usages(sema)
169+
.at_least_one();
170+
if used_param { None } else { Some(closure.body()?) }
171+
}
172+
None => Some(closure.body()?),
173+
Some(_) => None,
174+
}
159175
} else {
160176
None
161177
}
162178
})()
163-
.unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())).into())
179+
.unwrap_or_else(|| {
180+
let callable = if needs_parens_in_call(param) {
181+
make::expr_paren(param.clone()).into()
182+
} else {
183+
param.clone()
184+
};
185+
make::expr_call(callable, make::arg_list(Vec::new())).into()
186+
})
164187
}
165188

166189
fn eager_method_name(name: &str) -> Option<&str> {
@@ -177,6 +200,12 @@ fn ends_is(name: &str, end: &str) -> bool {
177200
name.strip_suffix(end).is_some_and(|s| s.is_empty() || s.ends_with('_'))
178201
}
179202

203+
fn needs_parens_in_call(param: &Expr) -> bool {
204+
let call = make::expr_call(make::ext::expr_unit(), make::arg_list(Vec::new()));
205+
let callable = call.expr().expect("invalid make call");
206+
param.needs_parens_in_place_of(call.syntax(), callable.syntax())
207+
}
208+
180209
#[cfg(test)]
181210
mod tests {
182211
use crate::tests::check_assist;
@@ -333,7 +362,7 @@ fn foo() {
333362
r#"
334363
fn foo() {
335364
let foo = Some("foo");
336-
return foo.and_then(|| Some("bar"));
365+
return foo.and_then(|it| Some("bar"));
337366
}
338367
"#,
339368
)
@@ -347,7 +376,7 @@ fn foo() {
347376
//- minicore: option, fn
348377
fn foo() {
349378
let foo = Some("foo");
350-
return foo.and_then$0(|| Some("bar"));
379+
return foo.and_then$0(|it| Some("bar"));
351380
}
352381
"#,
353382
r#"
@@ -359,6 +388,26 @@ fn foo() {
359388
)
360389
}
361390

391+
#[test]
392+
fn replace_and_then_with_and_used_param() {
393+
check_assist(
394+
replace_with_eager_method,
395+
r#"
396+
//- minicore: option, fn
397+
fn foo() {
398+
let foo = Some("foo");
399+
return foo.and_then$0(|it| Some(it.strip_suffix("bar")));
400+
}
401+
"#,
402+
r#"
403+
fn foo() {
404+
let foo = Some("foo");
405+
return foo.and((|it| Some(it.strip_suffix("bar")))());
406+
}
407+
"#,
408+
)
409+
}
410+
362411
#[test]
363412
fn replace_then_some_with_then() {
364413
check_assist(
@@ -395,6 +444,30 @@ fn foo() {
395444
let foo = true;
396445
let x = foo.then_some(2);
397446
}
447+
"#,
448+
)
449+
}
450+
451+
#[test]
452+
fn replace_then_with_then_some_needs_parens() {
453+
check_assist(
454+
replace_with_eager_method,
455+
r#"
456+
//- minicore: option, fn, bool_impl
457+
struct Func { f: fn() -> i32 }
458+
fn foo() {
459+
let foo = true;
460+
let func = Func { f: || 2 };
461+
let x = foo.then$0(func.f);
462+
}
463+
"#,
464+
r#"
465+
struct Func { f: fn() -> i32 }
466+
fn foo() {
467+
let foo = true;
468+
let func = Func { f: || 2 };
469+
let x = foo.then_some((func.f)());
470+
}
398471
"#,
399472
)
400473
}

0 commit comments

Comments
 (0)