Skip to content

Commit 9e20f4b

Browse files
committed
Auto merge of rust-lang#17187 - roife:fix-issue-17185, r=Veykril
fix: keep parentheses when the precedence of inner expr is lower than the outer one fix rust-lang#17185 Additionally, this PR simplifies some code in `apply_demorgan`.
2 parents 6a3556b + 723467b commit 9e20f4b

File tree

1 file changed

+48
-46
lines changed

1 file changed

+48
-46
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ use ide_db::{
88
};
99
use syntax::{
1010
ast::{self, make, AstNode, Expr::BinExpr, HasArgList},
11-
ted::{self, Position},
12-
SyntaxKind,
11+
ted, SyntaxKind, T,
1312
};
1413

1514
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
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)]
275259
mod 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

Comments
 (0)