1
- use hir:: { known, Semantics } ;
1
+ use hir:: { known, AsAssocItem , Semantics } ;
2
2
use ide_db:: {
3
3
helpers:: { for_each_tail_expr, FamousDefs } ,
4
4
RootDatabase ,
5
5
} ;
6
+ use itertools:: Itertools ;
6
7
use syntax:: {
7
- ast:: { self , make, ArgListOwner } ,
8
+ ast:: { self , edit :: AstNodeEdit , make, ArgListOwner } ,
8
9
ted, AstNode , SyntaxNode ,
9
10
} ;
10
11
@@ -75,6 +76,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
75
76
|builder| {
76
77
let closure_body = closure_body. clone_for_update ( ) ;
77
78
// Rewrite all `Some(e)` in tail position to `e`
79
+ let mut replacements = Vec :: new ( ) ;
78
80
for_each_tail_expr ( & closure_body, & mut |e| {
79
81
let e = match e {
80
82
ast:: Expr :: BreakExpr ( e) => e. expr ( ) ,
@@ -84,11 +86,12 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
84
86
if let Some ( ast:: Expr :: CallExpr ( call) ) = e {
85
87
if let Some ( arg_list) = call. arg_list ( ) {
86
88
if let Some ( arg) = arg_list. args ( ) . next ( ) {
87
- ted :: replace ( call. syntax ( ) , arg. syntax ( ) ) ;
89
+ replacements . push ( ( call. syntax ( ) . clone ( ) , arg. syntax ( ) . clone ( ) ) ) ;
88
90
}
89
91
}
90
92
}
91
93
} ) ;
94
+ replacements. into_iter ( ) . for_each ( |( old, new) | ted:: replace ( old, new) ) ;
92
95
let closure_body = match closure_body {
93
96
ast:: Expr :: BlockExpr ( block) => unwrap_trivial_block ( block) ,
94
97
e => e,
@@ -102,6 +105,95 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
102
105
)
103
106
}
104
107
108
+ // Assist: convert_bool_then_to_if
109
+ //
110
+ // Converts a `bool::then` method call to an equivalent if expression.
111
+ //
112
+ // ```
113
+ // # //- minicore: bool_impl
114
+ // fn main() {
115
+ // (0 == 0).then$0(|| val)
116
+ // }
117
+ // ```
118
+ // ->
119
+ // ```
120
+ // fn main() {
121
+ // if 0 == 0 {
122
+ // Some(val)
123
+ // } else {
124
+ // None
125
+ // }
126
+ // }
127
+ // ```
128
+ pub ( crate ) fn convert_bool_then_to_if ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
129
+ let name_ref = ctx. find_node_at_offset :: < ast:: NameRef > ( ) ?;
130
+ let mcall = name_ref. syntax ( ) . parent ( ) . and_then ( ast:: MethodCallExpr :: cast) ?;
131
+ let receiver = mcall. receiver ( ) ?;
132
+ let closure_body = mcall. arg_list ( ) ?. args ( ) . exactly_one ( ) . ok ( ) ?;
133
+ let closure_body = match closure_body {
134
+ ast:: Expr :: ClosureExpr ( expr) => expr. body ( ) ?,
135
+ _ => return None ,
136
+ } ;
137
+ // Verify this is `bool::then` that is being called.
138
+ let func = ctx. sema . resolve_method_call ( & mcall) ?;
139
+ if func. name ( ctx. sema . db ) . to_string ( ) != "then" {
140
+ return None ;
141
+ }
142
+ let assoc = func. as_assoc_item ( ctx. sema . db ) ?;
143
+ match assoc. container ( ctx. sema . db ) {
144
+ hir:: AssocItemContainer :: Impl ( impl_) if impl_. self_ty ( ctx. sema . db ) . is_bool ( ) => { }
145
+ _ => return None ,
146
+ }
147
+
148
+ let target = mcall. syntax ( ) . text_range ( ) ;
149
+ acc. add (
150
+ AssistId ( "convert_bool_then_to_if" , AssistKind :: RefactorRewrite ) ,
151
+ "Convert `bool::then` call to `if`" ,
152
+ target,
153
+ |builder| {
154
+ let closure_body = match closure_body {
155
+ ast:: Expr :: BlockExpr ( block) => block,
156
+ e => make:: block_expr ( None , Some ( e) ) ,
157
+ } ;
158
+
159
+ let closure_body = closure_body. clone_for_update ( ) ;
160
+ // Wrap all tails in `Some(...)`
161
+ let none_path = make:: expr_path ( make:: ext:: ident_path ( "None" ) ) ;
162
+ let some_path = make:: expr_path ( make:: ext:: ident_path ( "Some" ) ) ;
163
+ let mut replacements = Vec :: new ( ) ;
164
+ for_each_tail_expr ( & ast:: Expr :: BlockExpr ( closure_body. clone ( ) ) , & mut |e| {
165
+ let e = match e {
166
+ ast:: Expr :: BreakExpr ( e) => e. expr ( ) ,
167
+ ast:: Expr :: ReturnExpr ( e) => e. expr ( ) ,
168
+ _ => Some ( e. clone ( ) ) ,
169
+ } ;
170
+ if let Some ( expr) = e {
171
+ replacements. push ( (
172
+ expr. syntax ( ) . clone ( ) ,
173
+ make:: expr_call ( some_path. clone ( ) , make:: arg_list ( Some ( expr) ) )
174
+ . syntax ( )
175
+ . clone_for_update ( ) ,
176
+ ) ) ;
177
+ }
178
+ } ) ;
179
+ replacements. into_iter ( ) . for_each ( |( old, new) | ted:: replace ( old, new) ) ;
180
+
181
+ let cond = match & receiver {
182
+ ast:: Expr :: ParenExpr ( expr) => expr. expr ( ) . unwrap_or ( receiver) ,
183
+ _ => receiver,
184
+ } ;
185
+ let if_expr = make:: expr_if (
186
+ make:: condition ( cond, None ) ,
187
+ closure_body. reset_indent ( ) ,
188
+ Some ( ast:: ElseBranch :: Block ( make:: block_expr ( None , Some ( none_path) ) ) ) ,
189
+ )
190
+ . indent ( mcall. indent_level ( ) ) ;
191
+
192
+ builder. replace ( target, if_expr. to_string ( ) ) ;
193
+ } ,
194
+ )
195
+ }
196
+
105
197
fn option_variants (
106
198
sema : & Semantics < RootDatabase > ,
107
199
expr : & SyntaxNode ,
@@ -346,6 +438,113 @@ fn main() {
346
438
None
347
439
}
348
440
}
441
+ " ,
442
+ ) ;
443
+ }
444
+
445
+ #[ test]
446
+ fn convert_bool_then_to_if_inapplicable ( ) {
447
+ check_assist_not_applicable (
448
+ convert_bool_then_to_if,
449
+ r"
450
+ //- minicore:bool_impl
451
+ fn main() {
452
+ 0.t$0hen(|| 15);
453
+ }
454
+ " ,
455
+ ) ;
456
+ check_assist_not_applicable (
457
+ convert_bool_then_to_if,
458
+ r"
459
+ //- minicore:bool_impl
460
+ fn main() {
461
+ true.t$0hen(15);
462
+ }
463
+ " ,
464
+ ) ;
465
+ check_assist_not_applicable (
466
+ convert_bool_then_to_if,
467
+ r"
468
+ //- minicore:bool_impl
469
+ fn main() {
470
+ true.t$0hen(|| 15, 15);
471
+ }
472
+ " ,
473
+ ) ;
474
+ }
475
+
476
+ #[ test]
477
+ fn convert_bool_then_to_if_simple ( ) {
478
+ check_assist (
479
+ convert_bool_then_to_if,
480
+ r"
481
+ //- minicore:bool_impl
482
+ fn main() {
483
+ true.t$0hen(|| 15)
484
+ }
485
+ " ,
486
+ r"
487
+ fn main() {
488
+ if true {
489
+ Some(15)
490
+ } else {
491
+ None
492
+ }
493
+ }
494
+ " ,
495
+ ) ;
496
+ check_assist (
497
+ convert_bool_then_to_if,
498
+ r"
499
+ //- minicore:bool_impl
500
+ fn main() {
501
+ true.t$0hen(|| {
502
+ 15
503
+ })
504
+ }
505
+ " ,
506
+ r"
507
+ fn main() {
508
+ if true {
509
+ Some(15)
510
+ } else {
511
+ None
512
+ }
513
+ }
514
+ " ,
515
+ ) ;
516
+ }
517
+
518
+ #[ test]
519
+ fn convert_bool_then_to_if_tails ( ) {
520
+ check_assist (
521
+ convert_bool_then_to_if,
522
+ r"
523
+ //- minicore:bool_impl
524
+ fn main() {
525
+ true.t$0hen(|| {
526
+ loop {
527
+ if false {
528
+ break 0;
529
+ }
530
+ break 15;
531
+ }
532
+ })
533
+ }
534
+ " ,
535
+ r"
536
+ fn main() {
537
+ if true {
538
+ loop {
539
+ if false {
540
+ break Some(0);
541
+ }
542
+ break Some(15);
543
+ }
544
+ } else {
545
+ None
546
+ }
547
+ }
349
548
" ,
350
549
) ;
351
550
}
0 commit comments