1
1
use either:: Either :: { self , Left , Right } ;
2
2
use ide_db:: assists:: AssistId ;
3
+ use itertools:: Itertools ;
4
+ use syntax:: NodeOrToken :: { Node , Token } ;
5
+ use syntax:: SyntaxNode ;
3
6
use syntax:: ast:: edit_in_place:: Indent ;
4
7
use syntax:: syntax_editor:: SyntaxEditor ;
5
8
use syntax:: {
@@ -30,13 +33,14 @@ use crate::assist_context::{AssistContext, Assists};
30
33
// }
31
34
// ```
32
35
pub ( crate ) fn convert_attr_cfg_to_if ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
33
- let ( origin, cfg, stmt) = find_stmt ( ctx) ?;
36
+ let ( origin, cfg, stmt) = find_stmt ( ctx. find_node_at_offset :: < ast :: Attr > ( ) ? ) ?;
34
37
35
- if cfg . path ( ) ? . as_single_name_ref ( ) ? . text ( ) != " cfg" {
38
+ if ! is_cfg ( & cfg) {
36
39
return None ;
37
40
}
38
41
39
- let cfg_expr = make:: expr_macro ( cfg. path ( ) ?, cfg. token_tree ( ) ?) . into ( ) ;
42
+ let cfg_tt = cfg. token_tree ( ) ?;
43
+ let cfg_expr = make:: expr_macro ( cfg. path ( ) ?, cfg_tt. clone ( ) ) . into ( ) ;
40
44
41
45
acc. add (
42
46
AssistId :: refactor_rewrite ( "convert_attr_cfg_to_if" ) ,
@@ -45,14 +49,10 @@ pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>)
45
49
|builder| {
46
50
let mut edit = builder. make_editor ( origin. syntax ( ) ) ;
47
51
48
- let block = match stmt {
49
- Left ( stmt_list) => {
50
- remove_cfg ( & cfg, & mut edit) ;
51
- make_block ( stmt_list. statements ( ) , stmt_list. tail_expr ( ) )
52
- }
53
- Right ( stmt) => make_block ( [ stmt] , None ) ,
54
- } ;
55
- let if_expr = make:: expr_if ( cfg_expr, block, None ) . clone_for_update ( ) ;
52
+ let block = process_stmt ( & cfg, stmt, & mut edit) ;
53
+ let else_branch = take_else_branch ( & origin, cfg_tt, & mut edit) ;
54
+ let if_expr = make:: expr_if ( cfg_expr, block, else_branch) . clone_for_update ( ) ;
55
+
56
56
if_expr. indent ( origin. indent_level ( ) ) ;
57
57
edit. replace ( origin. syntax ( ) , if_expr. syntax ( ) ) ;
58
58
@@ -61,6 +61,21 @@ pub(crate) fn convert_attr_cfg_to_if(acc: &mut Assists, ctx: &AssistContext<'_>)
61
61
)
62
62
}
63
63
64
+ fn take_else_branch (
65
+ origin : & impl AstNode ,
66
+ cfg_tt : ast:: TokenTree ,
67
+ edit : & mut SyntaxEditor ,
68
+ ) -> Option < ast:: ElseBranch > {
69
+ let attr_cfg_not = next_attr_cfg_not ( origin, & cfg_tt) ?;
70
+ let ( else_origin, else_cfg, else_stmt) = find_stmt ( attr_cfg_not) ?;
71
+ let block = process_stmt ( & else_cfg, else_stmt, edit) ;
72
+
73
+ remove_next_ws ( origin. syntax ( ) , edit) ;
74
+ edit. delete ( else_origin. syntax ( ) ) ;
75
+
76
+ Some ( ast:: ElseBranch :: Block ( block) )
77
+ }
78
+
64
79
fn make_block (
65
80
stmts : impl IntoIterator < Item = ast:: Stmt > ,
66
81
tail_expr : Option < ast:: Expr > ,
@@ -71,15 +86,27 @@ fn make_block(
71
86
make:: block_expr ( stmts, tail_expr)
72
87
}
73
88
89
+ fn process_stmt (
90
+ cfg : & ast:: Attr ,
91
+ stmt : Either < ast:: StmtList , ast:: Stmt > ,
92
+ edit : & mut SyntaxEditor ,
93
+ ) -> ast:: BlockExpr {
94
+ match stmt {
95
+ Left ( stmt_list) => {
96
+ remove_cfg ( cfg, edit) ;
97
+ make_block ( stmt_list. statements ( ) , stmt_list. tail_expr ( ) )
98
+ }
99
+ Right ( stmt) => make_block ( [ stmt] , None ) ,
100
+ }
101
+ }
102
+
74
103
fn find_stmt (
75
- ctx : & AssistContext < ' _ > ,
104
+ attr : ast :: Attr ,
76
105
) -> Option < ( impl Indent , ast:: Attr , Either < ast:: StmtList , ast:: Stmt > ) > {
77
- let attr = ctx. find_node_at_offset :: < ast:: Attr > ( ) ?;
78
-
79
106
if let Some ( stmt_list) = find_stmt_list ( & attr) {
80
107
let new_stmt_list = stmt_list. clone_for_update ( ) ;
81
108
new_stmt_list. dedent ( stmt_list. indent_level ( ) ) ;
82
- return Some ( ( Left ( stmt_list) , attr, Left ( new_stmt_list) ) ) ;
109
+ return Some ( ( Left ( stmt_list) , attr. clone ( ) , Left ( new_stmt_list) ) ) ;
83
110
}
84
111
85
112
let node =
@@ -115,13 +142,49 @@ fn find_stmt_list(attr: &ast::Attr) -> Option<ast::StmtList> {
115
142
AstNode :: cast ( node)
116
143
}
117
144
145
+ fn next_attr_cfg_not ( node : & impl AstNode , cond : & ast:: TokenTree ) -> Option < ast:: Attr > {
146
+ let attr = node
147
+ . syntax ( )
148
+ . ancestors ( )
149
+ . filter_map ( |node| node. next_sibling ( ) )
150
+ . flat_map ( |x| x. descendants ( ) )
151
+ . filter_map ( ast:: Attr :: cast)
152
+ . find ( is_cfg) ?;
153
+
154
+ let tts = attr
155
+ . token_tree ( ) ?
156
+ . token_trees_and_tokens ( )
157
+ . filter ( |tt| tt. as_token ( ) . is_none_or ( |t| !t. kind ( ) . is_trivia ( ) ) )
158
+ . collect_array ( ) ?;
159
+ if let [ Token ( _lparen) , Token ( not) , Node ( not_cond) , Token ( _rparen) ] = tts
160
+ && not. text ( ) == "not"
161
+ && not_cond. syntax ( ) . text ( ) == cond. syntax ( ) . text ( )
162
+ {
163
+ Some ( attr)
164
+ } else {
165
+ None
166
+ }
167
+ }
168
+
118
169
fn remove_cfg ( cfg : & ast:: Attr , edit : & mut SyntaxEditor ) {
119
- if let Some ( next) = cfg. syntax ( ) . last_token ( ) . and_then ( |it| it. next_token ( ) )
170
+ remove_next_ws ( cfg. syntax ( ) , edit) ;
171
+ edit. delete ( cfg. syntax ( ) ) ;
172
+ }
173
+
174
+ fn remove_next_ws ( node : & SyntaxNode , edit : & mut SyntaxEditor ) {
175
+ if let Some ( Token ( next) ) = node. next_sibling_or_token ( )
120
176
&& next. kind ( ) == SyntaxKind :: WHITESPACE
121
177
{
122
178
edit. delete ( next) ;
179
+ } else if let Some ( parent) = node. parent ( )
180
+ && parent. last_child ( ) . is_some_and ( |it| it == * node)
181
+ {
182
+ remove_next_ws ( & parent, edit) ;
123
183
}
124
- edit. delete ( cfg. syntax ( ) ) ;
184
+ }
185
+
186
+ fn is_cfg ( attr : & ast:: Attr ) -> bool {
187
+ attr. path ( ) . and_then ( |p| p. as_single_name_ref ( ) ) . is_some_and ( |name| name. text ( ) == "cfg" )
125
188
}
126
189
127
190
#[ cfg( test) ]
@@ -154,6 +217,38 @@ fn foo() {
154
217
) ;
155
218
}
156
219
220
+ #[ test]
221
+ fn test_stmt_list_else ( ) {
222
+ check_assist (
223
+ convert_attr_cfg_to_if,
224
+ r#"
225
+ fn foo() {
226
+ $0#[cfg(feature = "foo")]
227
+ {
228
+ let x = 2;
229
+ let _ = x+1;
230
+ }
231
+ #[cfg(not(feature = "foo"))]
232
+ {
233
+ let _ = 3;
234
+ }
235
+ // needless comment
236
+ }
237
+ "# ,
238
+ r#"
239
+ fn foo() {
240
+ if cfg!(feature = "foo") {
241
+ let x = 2;
242
+ let _ = x+1;
243
+ } else {
244
+ let _ = 3;
245
+ }
246
+ // needless comment
247
+ }
248
+ "# ,
249
+ ) ;
250
+ }
251
+
157
252
#[ test]
158
253
fn test_expr_stmt ( ) {
159
254
check_assist (
@@ -218,6 +313,35 @@ fn foo() {
218
313
#[allow(unused)]
219
314
bar();
220
315
}
316
+ }
317
+ "# ,
318
+ ) ;
319
+
320
+ check_assist (
321
+ convert_attr_cfg_to_if,
322
+ r#"
323
+ fn bar() {}
324
+ fn baz() {}
325
+ fn foo() {
326
+ #[allow(unused)]
327
+ $0#[cfg(feature = "foo")]
328
+ bar();
329
+ #[allow(unused)]
330
+ #[cfg(not(feature = "foo"))]
331
+ baz();
332
+ }
333
+ "# ,
334
+ r#"
335
+ fn bar() {}
336
+ fn baz() {}
337
+ fn foo() {
338
+ if cfg!(feature = "foo") {
339
+ #[allow(unused)]
340
+ bar();
341
+ } else {
342
+ #[allow(unused)]
343
+ baz();
344
+ }
221
345
}
222
346
"# ,
223
347
) ;
@@ -264,6 +388,73 @@ mod a {
264
388
}
265
389
}
266
390
}
391
+ }
392
+ "# ,
393
+ ) ;
394
+
395
+ check_assist (
396
+ convert_attr_cfg_to_if,
397
+ r#"
398
+ mod a {
399
+ fn foo() {
400
+ #[allow(unused)]
401
+ $0#[cfg(feature = "foo")]
402
+ {
403
+ let _ = match () {
404
+ () => {
405
+ todo!()
406
+ },
407
+ };
408
+ match () {
409
+ () => {
410
+ todo!()
411
+ },
412
+ }
413
+ }
414
+ #[cfg(not(feature = "foo"))]
415
+ {
416
+ let _ = match () {
417
+ () => {
418
+ todo!("")
419
+ },
420
+ };
421
+ match () {
422
+ () => {
423
+ todo!("")
424
+ },
425
+ }
426
+ }
427
+ }
428
+ }
429
+ "# ,
430
+ r#"
431
+ mod a {
432
+ fn foo() {
433
+ #[allow(unused)]
434
+ if cfg!(feature = "foo") {
435
+ let _ = match () {
436
+ () => {
437
+ todo!()
438
+ },
439
+ };
440
+ match () {
441
+ () => {
442
+ todo!()
443
+ },
444
+ }
445
+ } else {
446
+ let _ = match () {
447
+ () => {
448
+ todo!("")
449
+ },
450
+ };
451
+ match () {
452
+ () => {
453
+ todo!("")
454
+ },
455
+ }
456
+ }
457
+ }
267
458
}
268
459
"# ,
269
460
) ;
@@ -327,6 +518,47 @@ mod a {
327
518
};
328
519
}
329
520
}
521
+ }
522
+ "# ,
523
+ ) ;
524
+
525
+ check_assist (
526
+ convert_attr_cfg_to_if,
527
+ r#"
528
+ mod a {
529
+ fn foo() {
530
+ $0#[cfg(feature = "foo")]
531
+ match () {
532
+ () => {
533
+ todo!()
534
+ },
535
+ };
536
+ #[cfg(not(feature = "foo"))]
537
+ match () {
538
+ () => {
539
+ todo!("")
540
+ },
541
+ };
542
+ }
543
+ }
544
+ "# ,
545
+ r#"
546
+ mod a {
547
+ fn foo() {
548
+ if cfg!(feature = "foo") {
549
+ match () {
550
+ () => {
551
+ todo!()
552
+ },
553
+ };
554
+ } else {
555
+ match () {
556
+ () => {
557
+ todo!("")
558
+ },
559
+ };
560
+ }
561
+ }
330
562
}
331
563
"# ,
332
564
) ;
0 commit comments