@@ -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 :: * ;
@@ -375,6 +359,21 @@ fn f() { !(S <= S || S < S) }
375359 ) ;
376360 }
377361
362+ #[ test]
363+ fn demorgan_keep_pars_for_op_precedence2 ( ) {
364+ cov_mark:: check!( demorgan_keep_parens_for_op_precedence2) ;
365+ check_assist (
366+ apply_demorgan,
367+ "fn f() { (a && !(b &&$0 c); }" ,
368+ "fn f() { (a && (!b || !c); }" ,
369+ ) ;
370+ }
371+
372+ #[ test]
373+ fn demorgan_keep_pars_for_op_precedence3 ( ) {
374+ check_assist ( apply_demorgan, "fn f() { (a || !(b &&$0 c); }" , "fn f() { (a || !b || !c; }" ) ;
375+ }
376+
378377 #[ test]
379378 fn demorgan_removes_pars_in_eq_precedence ( ) {
380379 check_assist (
@@ -384,6 +383,11 @@ fn f() { !(S <= S || S < S) }
384383 )
385384 }
386385
386+ #[ test]
387+ fn demorgan_removes_pars_for_op_precedence2 ( ) {
388+ check_assist ( apply_demorgan, "fn f() { (a || !(b ||$0 c); }" , "fn f() { (a || !b && !c; }" ) ;
389+ }
390+
387391 #[ test]
388392 fn demorgan_iterator_any_all_reverse ( ) {
389393 check_assist (
0 commit comments