@@ -8,8 +8,7 @@ use ide_db::{
88} ;
99use syntax:: {
1010 ast:: { self , make, AstNode , Expr :: BinExpr , HasArgList } ,
11- ted:: { self , Position } ,
12- SyntaxKind ,
11+ ted, SyntaxKind , T ,
1312} ;
1413
1514use crate :: { utils:: invert_boolean_expression, AssistContext , AssistId , AssistKind , Assists } ;
@@ -62,7 +61,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
6261 let demorganed = bin_expr. clone_subtree ( ) . clone_for_update ( ) ;
6362
6463 ted:: replace ( demorganed. op_token ( ) ?, ast:: make:: token ( inv_token) ) ;
65- let mut exprs = VecDeque :: from ( vec ! [
64+ let mut exprs = VecDeque :: from ( [
6665 ( bin_expr. lhs ( ) ?, demorganed. lhs ( ) ?) ,
6766 ( bin_expr. rhs ( ) ?, demorganed. rhs ( ) ?) ,
6867 ] ) ;
@@ -93,58 +92,38 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
9392 }
9493 }
9594
96- let dm_lhs = demorganed. lhs ( ) ?;
97-
9895 acc. add_group (
9996 & GroupLabel ( "Apply De Morgan's law" . to_owned ( ) ) ,
10097 AssistId ( "apply_demorgan" , AssistKind :: RefactorRewrite ) ,
10198 "Apply De Morgan's law" ,
10299 op_range,
103100 |edit| {
101+ let demorganed = ast:: Expr :: BinExpr ( demorganed) ;
104102 let paren_expr = bin_expr. syntax ( ) . parent ( ) . and_then ( ast:: ParenExpr :: cast) ;
105103 let neg_expr = paren_expr
106104 . clone ( )
107105 . and_then ( |paren_expr| paren_expr. syntax ( ) . parent ( ) )
108106 . 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 ) ;
116109
117110 if let Some ( paren_expr) = paren_expr {
118111 if let Some ( neg_expr) = neg_expr {
119112 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+ } ;
121121 } else {
122122 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) ) ;
137124 }
138125 } 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) ) ;
148127 }
149128 } ,
150129 )
@@ -271,6 +250,11 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
271250 }
272251}
273252
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+
274258#[ cfg( test) ]
275259mod tests {
276260 use super :: * ;
@@ -349,16 +333,14 @@ fn f() { !(S <= S || S < S) }
349333 check_assist ( apply_demorgan, "fn f() { (x ||$0 x) }" , "fn f() { !(!x && !x) }" )
350334 }
351335
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+ }
362344
363345 #[ test]
364346 fn demorgan_keep_pars_for_op_precedence ( ) {
@@ -375,6 +357,21 @@ fn f() { !(S <= S || S < S) }
375357 ) ;
376358 }
377359
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+
378375 #[ test]
379376 fn demorgan_removes_pars_in_eq_precedence ( ) {
380377 check_assist (
@@ -384,6 +381,11 @@ fn f() { !(S <= S || S < S) }
384381 )
385382 }
386383
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+
387389 #[ test]
388390 fn demorgan_iterator_any_all_reverse ( ) {
389391 check_assist (
0 commit comments