@@ -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