1- use ide_db:: assists:: AssistId ;
1+ use hir:: Semantics ;
2+ use ide_db:: { RootDatabase , assists:: AssistId , defs:: Definition } ;
23use 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
166189fn 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) ]
181210mod tests {
182211 use crate :: tests:: check_assist;
@@ -333,7 +362,7 @@ fn foo() {
333362 r#"
334363fn 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
348377fn 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