@@ -8,8 +8,7 @@ use ide_db::{
8
8
} ;
9
9
use syntax:: {
10
10
ast:: { self , make, AstNode , Expr :: BinExpr , HasArgList } ,
11
- ted:: { self , Position } ,
12
- SyntaxKind ,
11
+ ted, SyntaxKind , T ,
13
12
} ;
14
13
15
14
use crate :: { utils:: invert_boolean_expression, AssistContext , AssistId , AssistKind , Assists } ;
@@ -62,7 +61,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
62
61
let demorganed = bin_expr. clone_subtree ( ) . clone_for_update ( ) ;
63
62
64
63
ted:: replace ( demorganed. op_token ( ) ?, ast:: make:: token ( inv_token) ) ;
65
- let mut exprs = VecDeque :: from ( vec ! [
64
+ let mut exprs = VecDeque :: from ( [
66
65
( bin_expr. lhs ( ) ?, demorganed. lhs ( ) ?) ,
67
66
( bin_expr. rhs ( ) ?, demorganed. rhs ( ) ?) ,
68
67
] ) ;
@@ -93,58 +92,38 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
93
92
}
94
93
}
95
94
96
- let dm_lhs = demorganed. lhs ( ) ?;
97
-
98
95
acc. add_group (
99
96
& GroupLabel ( "Apply De Morgan's law" . to_owned ( ) ) ,
100
97
AssistId ( "apply_demorgan" , AssistKind :: RefactorRewrite ) ,
101
98
"Apply De Morgan's law" ,
102
99
op_range,
103
100
|edit| {
101
+ let demorganed = ast:: Expr :: BinExpr ( demorganed) ;
104
102
let paren_expr = bin_expr. syntax ( ) . parent ( ) . and_then ( ast:: ParenExpr :: cast) ;
105
103
let neg_expr = paren_expr
106
104
. clone ( )
107
105
. and_then ( |paren_expr| paren_expr. syntax ( ) . parent ( ) )
108
106
. and_then ( ast:: PrefixExpr :: cast)
109
- . and_then ( |prefix_expr| {
110
- if prefix_expr. op_kind ( ) ? == ast:: UnaryOp :: Not {
111
- Some ( prefix_expr)
112
- } else {
113
- None
114
- }
115
- } ) ;
107
+ . filter ( |prefix_expr| matches ! ( prefix_expr. op_kind( ) , Some ( ast:: UnaryOp :: Not ) ) )
108
+ . map ( ast:: Expr :: PrefixExpr ) ;
116
109
117
110
if let Some ( paren_expr) = paren_expr {
118
111
if let Some ( neg_expr) = neg_expr {
119
112
cov_mark:: hit!( demorgan_double_negation) ;
120
- edit. replace_ast ( ast:: Expr :: PrefixExpr ( neg_expr) , demorganed. into ( ) ) ;
113
+ let parent = neg_expr. syntax ( ) . parent ( ) ;
114
+
115
+ if parent. is_some_and ( |parent| demorganed. needs_parens_in ( parent) ) {
116
+ cov_mark:: hit!( demorgan_keep_parens_for_op_precedence2) ;
117
+ edit. replace_ast ( neg_expr, make:: expr_paren ( demorganed) ) ;
118
+ } else {
119
+ edit. replace_ast ( neg_expr, demorganed) ;
120
+ } ;
121
121
} else {
122
122
cov_mark:: hit!( demorgan_double_parens) ;
123
- ted:: insert_all_raw (
124
- Position :: before ( dm_lhs. syntax ( ) ) ,
125
- vec ! [
126
- syntax:: NodeOrToken :: Token ( ast:: make:: token( SyntaxKind :: BANG ) ) ,
127
- syntax:: NodeOrToken :: Token ( ast:: make:: token( SyntaxKind :: L_PAREN ) ) ,
128
- ] ,
129
- ) ;
130
-
131
- ted:: append_child_raw (
132
- demorganed. syntax ( ) ,
133
- syntax:: NodeOrToken :: Token ( ast:: make:: token ( SyntaxKind :: R_PAREN ) ) ,
134
- ) ;
135
-
136
- edit. replace_ast ( ast:: Expr :: ParenExpr ( paren_expr) , demorganed. into ( ) ) ;
123
+ edit. replace_ast ( paren_expr. into ( ) , add_bang_paren ( demorganed) ) ;
137
124
}
138
125
} else {
139
- ted:: insert_all_raw (
140
- Position :: before ( dm_lhs. syntax ( ) ) ,
141
- vec ! [
142
- syntax:: NodeOrToken :: Token ( ast:: make:: token( SyntaxKind :: BANG ) ) ,
143
- syntax:: NodeOrToken :: Token ( ast:: make:: token( SyntaxKind :: L_PAREN ) ) ,
144
- ] ,
145
- ) ;
146
- ted:: append_child_raw ( demorganed. syntax ( ) , ast:: make:: token ( SyntaxKind :: R_PAREN ) ) ;
147
- edit. replace_ast ( bin_expr, demorganed) ;
126
+ edit. replace_ast ( bin_expr. into ( ) , add_bang_paren ( demorganed) ) ;
148
127
}
149
128
} ,
150
129
)
@@ -271,6 +250,11 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
271
250
}
272
251
}
273
252
253
+ /// Add bang and parentheses to the expression.
254
+ fn add_bang_paren ( expr : ast:: Expr ) -> ast:: Expr {
255
+ make:: expr_prefix ( T ! [ !] , make:: expr_paren ( expr) )
256
+ }
257
+
274
258
#[ cfg( test) ]
275
259
mod tests {
276
260
use super :: * ;
@@ -349,16 +333,14 @@ fn f() { !(S <= S || S < S) }
349
333
check_assist ( apply_demorgan, "fn f() { (x ||$0 x) }" , "fn f() { !(!x && !x) }" )
350
334
}
351
335
352
- // FIXME : This needs to go.
353
- // // https://github.com/rust-lang/rust-analyzer/issues/10963
354
- // #[test]
355
- // fn demorgan_doesnt_hang() {
356
- // check_assist(
357
- // apply_demorgan,
358
- // "fn f() { 1 || 3 &&$0 4 || 5 }",
359
- // "fn f() { !(!1 || !3 || !4) || 5 }",
360
- // )
361
- // }
336
+ #[ test]
337
+ fn demorgan_doesnt_hang ( ) {
338
+ check_assist (
339
+ apply_demorgan,
340
+ "fn f() { 1 || 3 &&$0 4 || 5 }" ,
341
+ "fn f() { 1 || !(!3 || !4) || 5 }" ,
342
+ )
343
+ }
362
344
363
345
#[ test]
364
346
fn demorgan_keep_pars_for_op_precedence ( ) {
@@ -375,6 +357,21 @@ fn f() { !(S <= S || S < S) }
375
357
) ;
376
358
}
377
359
360
+ #[ test]
361
+ fn demorgan_keep_pars_for_op_precedence2 ( ) {
362
+ cov_mark:: check!( demorgan_keep_parens_for_op_precedence2) ;
363
+ check_assist (
364
+ apply_demorgan,
365
+ "fn f() { (a && !(b &&$0 c); }" ,
366
+ "fn f() { (a && (!b || !c); }" ,
367
+ ) ;
368
+ }
369
+
370
+ #[ test]
371
+ fn demorgan_keep_pars_for_op_precedence3 ( ) {
372
+ check_assist ( apply_demorgan, "fn f() { (a || !(b &&$0 c); }" , "fn f() { (a || !b || !c; }" ) ;
373
+ }
374
+
378
375
#[ test]
379
376
fn demorgan_removes_pars_in_eq_precedence ( ) {
380
377
check_assist (
@@ -384,6 +381,11 @@ fn f() { !(S <= S || S < S) }
384
381
)
385
382
}
386
383
384
+ #[ test]
385
+ fn demorgan_removes_pars_for_op_precedence2 ( ) {
386
+ check_assist ( apply_demorgan, "fn f() { (a || !(b ||$0 c); }" , "fn f() { (a || !b && !c; }" ) ;
387
+ }
388
+
387
389
#[ test]
388
390
fn demorgan_iterator_any_all_reverse ( ) {
389
391
check_assist (
0 commit comments