aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/utils.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-24 12:19:22 +0000
committerGitHub <[email protected]>2021-02-24 12:19:22 +0000
commit0537510aef4059d5f79146a8dc2734ffdb27dc74 (patch)
tree6fa29e24165e07db8f77dad76d633b12eb79574f /crates/ide_assists/src/utils.rs
parent18ac02ba411e2f9eeb38c3b246c619ac76846767 (diff)
parent694f7a7e9f90cc435afcaade23b5908728d17ed2 (diff)
Merge #7719
7719: De Morgan's Law assist now correctly parenthesizes binary expressions. r=Veykril a=lbrande Closes #7694 by parenthesizing binary expressions that are negated. Co-authored-by: lbrande <[email protected]> Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide_assists/src/utils.rs')
-rw-r--r--crates/ide_assists/src/utils.rs50
1 files changed, 41 insertions, 9 deletions
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 0074da741..276792bc1 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -3,8 +3,11 @@
3use std::ops; 3use std::ops;
4 4
5use ast::TypeBoundsOwner; 5use ast::TypeBoundsOwner;
6use hir::{Adt, HasSource}; 6use hir::{Adt, HasSource, Semantics};
7use ide_db::{helpers::SnippetCap, RootDatabase}; 7use ide_db::{
8 helpers::{FamousDefs, SnippetCap},
9 RootDatabase,
10};
8use itertools::Itertools; 11use itertools::Itertools;
9use stdx::format_to; 12use stdx::format_to;
10use syntax::{ 13use syntax::{
@@ -205,23 +208,36 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
205 .unwrap_or_else(|| node.text_range().start()) 208 .unwrap_or_else(|| node.text_range().start())
206} 209}
207 210
208pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { 211pub(crate) fn invert_boolean_expression(
209 if let Some(expr) = invert_special_case(&expr) { 212 sema: &Semantics<RootDatabase>,
213 expr: ast::Expr,
214) -> ast::Expr {
215 if let Some(expr) = invert_special_case(sema, &expr) {
210 return expr; 216 return expr;
211 } 217 }
212 make::expr_prefix(T![!], expr) 218 make::expr_prefix(T![!], expr)
213} 219}
214 220
215fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { 221fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> {
216 match expr { 222 match expr {
217 ast::Expr::BinExpr(bin) => match bin.op_kind()? { 223 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
218 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), 224 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
219 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), 225 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
220 // Parenthesize composite boolean expressions before prefixing `!` 226 // Swap `<` with `>=`, `<=` with `>`, ... if operands `impl Ord`
221 ast::BinOp::BooleanAnd | ast::BinOp::BooleanOr => { 227 ast::BinOp::LesserTest if bin_impls_ord(sema, bin) => {
222 Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))) 228 bin.replace_op(T![>=]).map(|it| it.into())
229 }
230 ast::BinOp::LesserEqualTest if bin_impls_ord(sema, bin) => {
231 bin.replace_op(T![>]).map(|it| it.into())
232 }
233 ast::BinOp::GreaterTest if bin_impls_ord(sema, bin) => {
234 bin.replace_op(T![<=]).map(|it| it.into())
235 }
236 ast::BinOp::GreaterEqualTest if bin_impls_ord(sema, bin) => {
237 bin.replace_op(T![<]).map(|it| it.into())
223 } 238 }
224 _ => None, 239 // Parenthesize other expressions before prefixing `!`
240 _ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
225 }, 241 },
226 ast::Expr::MethodCallExpr(mce) => { 242 ast::Expr::MethodCallExpr(mce) => {
227 let receiver = mce.receiver()?; 243 let receiver = mce.receiver()?;
@@ -250,6 +266,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
250 } 266 }
251} 267}
252 268
269fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool {
270 match (
271 bin.lhs().and_then(|lhs| sema.type_of_expr(&lhs)),
272 bin.rhs().and_then(|rhs| sema.type_of_expr(&rhs)),
273 ) {
274 (Some(lhs_ty), Some(rhs_ty)) if lhs_ty == rhs_ty => {
275 let krate = sema.scope(bin.syntax()).module().map(|it| it.krate());
276 let ord_trait = FamousDefs(sema, krate).core_cmp_Ord();
277 ord_trait.map_or(false, |ord_trait| {
278 lhs_ty.autoderef(sema.db).any(|ty| ty.impls_trait(sema.db, ord_trait, &[]))
279 })
280 }
281 _ => false,
282 }
283}
284
253pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 285pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
254 [Direction::Next, Direction::Prev].iter().copied() 286 [Direction::Next, Direction::Prev].iter().copied()
255} 287}