Skip to content

Commit 76567ee

Browse files
authored
Merge pull request #19251 from Veykril/push-tkmpqtzxynxk
Remove syntax editing from parenthesis computation
2 parents 2fabcf0 + 570c6ad commit 76567ee

File tree

6 files changed

+50
-87
lines changed

6 files changed

+50
-87
lines changed

crates/ide-assists/src/handlers/apply_demorgan.rs

+24-41
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ use ide_db::{
66
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
77
};
88
use syntax::{
9-
ast::{self, syntax_factory::SyntaxFactory, AstNode, Expr::BinExpr, HasArgList},
9+
ast::{
10+
self,
11+
prec::{precedence, ExprPrecedence},
12+
syntax_factory::SyntaxFactory,
13+
AstNode,
14+
Expr::BinExpr,
15+
HasArgList,
16+
},
1017
syntax_editor::{Position, SyntaxEditor},
11-
SyntaxKind, SyntaxNode, T,
18+
SyntaxKind, T,
1219
};
1320

1421
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
@@ -52,9 +59,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
5259
}
5360

5461
let op = bin_expr.op_kind()?;
55-
let inv_token = match op {
56-
ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2,
57-
ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2,
62+
let (inv_token, prec) = match op {
63+
ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
64+
ast::BinaryOp::LogicOp(ast::LogicOp::Or) => (SyntaxKind::AMP2, ExprPrecedence::LAnd),
5865
_ => return None,
5966
};
6067

@@ -65,33 +72,33 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
6572
editor.replace(demorganed.op_token()?, make.token(inv_token));
6673

6774
let mut exprs = VecDeque::from([
68-
(bin_expr.lhs()?, demorganed.lhs()?),
69-
(bin_expr.rhs()?, demorganed.rhs()?),
75+
(bin_expr.lhs()?, demorganed.lhs()?, prec),
76+
(bin_expr.rhs()?, demorganed.rhs()?, prec),
7077
]);
7178

72-
while let Some((expr, dm)) = exprs.pop_front() {
79+
while let Some((expr, demorganed, prec)) = exprs.pop_front() {
7380
if let BinExpr(bin_expr) = &expr {
74-
if let BinExpr(cbin_expr) = &dm {
81+
if let BinExpr(cbin_expr) = &demorganed {
7582
if op == bin_expr.op_kind()? {
7683
editor.replace(cbin_expr.op_token()?, make.token(inv_token));
77-
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
78-
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
84+
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec));
85+
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec));
7986
} else {
8087
let mut inv = invert_boolean_expression(&make, expr);
81-
if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) {
88+
if precedence(&inv).needs_parentheses_in(prec) {
8289
inv = make.expr_paren(inv).into();
8390
}
84-
editor.replace(dm.syntax(), inv.syntax());
91+
editor.replace(demorganed.syntax(), inv.syntax());
8592
}
8693
} else {
8794
return None;
8895
}
8996
} else {
90-
let mut inv = invert_boolean_expression(&make, dm.clone());
91-
if needs_parens_in_place_of(&inv, &dm.syntax().parent()?, &dm) {
97+
let mut inv = invert_boolean_expression(&make, demorganed.clone());
98+
if precedence(&inv).needs_parentheses_in(prec) {
9299
inv = make.expr_paren(inv).into();
93100
}
94-
editor.replace(dm.syntax(), inv.syntax());
101+
editor.replace(demorganed.syntax(), inv.syntax());
95102
}
96103
}
97104

@@ -121,7 +128,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
121128
let parent = neg_expr.syntax().parent();
122129
editor = builder.make_editor(neg_expr.syntax());
123130

124-
if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) {
131+
if parent.is_some_and(|parent| demorganed.needs_parens_in(&parent)) {
125132
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
126133
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
127134
} else {
@@ -271,30 +278,6 @@ fn add_bang_paren(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr {
271278
make.expr_prefix(T![!], make.expr_paren(expr).into()).into()
272279
}
273280

274-
fn needs_parens_in_place_of(
275-
this: &ast::Expr,
276-
parent: &SyntaxNode,
277-
in_place_of: &ast::Expr,
278-
) -> bool {
279-
assert_eq!(Some(parent), in_place_of.syntax().parent().as_ref());
280-
281-
let child_idx = parent
282-
.children()
283-
.enumerate()
284-
.find_map(|(i, it)| if &it == in_place_of.syntax() { Some(i) } else { None })
285-
.unwrap();
286-
let parent = parent.clone_subtree();
287-
let subtree_place = parent.children().nth(child_idx).unwrap();
288-
289-
let mut editor = SyntaxEditor::new(parent);
290-
editor.replace(subtree_place, this.syntax());
291-
let edit = editor.finish();
292-
293-
let replaced = edit.new_root().children().nth(child_idx).unwrap();
294-
let replaced = ast::Expr::cast(replaced).unwrap();
295-
replaced.needs_parens_in(edit.new_root().clone())
296-
}
297-
298281
#[cfg(test)]
299282
mod tests {
300283
use super::*;

crates/ide-assists/src/handlers/inline_local_variable.rs

+3-17
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ use ide_db::{
55
EditionedFileId, RootDatabase,
66
};
77
use syntax::{
8-
ast::{
9-
self,
10-
prec::{precedence, ExprPrecedence},
11-
AstNode, AstToken, HasName,
12-
},
8+
ast::{self, AstNode, AstToken, HasName},
139
SyntaxElement, TextRange,
1410
};
1511

@@ -77,22 +73,12 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
7773
}
7874
let usage_node =
7975
name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind()));
80-
let usage_parent_option =
81-
usage_node.and_then(|it| it.parent()).and_then(ast::Expr::cast);
76+
let usage_parent_option = usage_node.and_then(|it| it.parent());
8277
let usage_parent = match usage_parent_option {
8378
Some(u) => u,
8479
None => return Some((range, name_ref, false)),
8580
};
86-
let initializer = precedence(&initializer_expr);
87-
let parent = precedence(&usage_parent);
88-
Some((
89-
range,
90-
name_ref,
91-
parent != ExprPrecedence::Unambiguous
92-
&& initializer < parent
93-
// initializer == ExprPrecedence::Prefix -> parent != ExprPrecedence::Jump
94-
&& (initializer != ExprPrecedence::Prefix || parent != ExprPrecedence::Jump),
95-
))
81+
Some((range, name_ref, initializer_expr.needs_parens_in(&usage_parent)))
9682
})
9783
.collect::<Option<Vec<_>>>()?;
9884

crates/ide-assists/src/handlers/remove_parentheses.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) ->
3434
let expr = parens.expr()?;
3535

3636
let parent = parens.syntax().parent()?;
37-
if expr.needs_parens_in(parent) {
37+
if expr.needs_parens_in(&parent) {
3838
return None;
3939
}
4040

crates/ide-assists/src/handlers/unqualify_method_call.rs

+2-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ide_db::imports::insert_use::ImportScope;
22
use syntax::{
3-
ast::{self, make, AstNode, HasArgList},
3+
ast::{self, prec::ExprPrecedence, AstNode, HasArgList},
44
TextRange,
55
};
66

@@ -55,7 +55,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
5555
TextRange::new(path.syntax().text_range().start(), l_paren.text_range().end());
5656

5757
// Parens around `expr` if needed
58-
let parens = needs_parens_as_receiver(&first_arg).then(|| {
58+
let parens = first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix).then(|| {
5959
let range = first_arg.syntax().text_range();
6060
(range.start(), range.end())
6161
});
@@ -124,24 +124,6 @@ fn add_import(
124124
}
125125
}
126126

127-
fn needs_parens_as_receiver(expr: &ast::Expr) -> bool {
128-
// Make `(expr).dummy()`
129-
let dummy_call = make::expr_method_call(
130-
make::expr_paren(expr.clone()),
131-
make::name_ref("dummy"),
132-
make::arg_list([]),
133-
);
134-
135-
// Get the `expr` clone with the right parent back
136-
// (unreachable!s are fine since we've just constructed the expression)
137-
let ast::Expr::MethodCallExpr(call) = &dummy_call else { unreachable!() };
138-
let Some(receiver) = call.receiver() else { unreachable!() };
139-
let ast::Expr::ParenExpr(parens) = receiver else { unreachable!() };
140-
let Some(expr) = parens.expr() else { unreachable!() };
141-
142-
expr.needs_parens_in(dummy_call.syntax().clone())
143-
}
144-
145127
#[cfg(test)]
146128
mod tests {
147129
use crate::tests::{check_assist, check_assist_not_applicable};

crates/ide/src/inlay_hints/adjustment.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,12 @@ fn mode_and_needs_parens_for_adjustment_hints(
258258
fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
259259
let prec = expr.precedence();
260260
if postfix {
261-
// postfix ops have higher precedence than any other operator, so we need to wrap
262-
// any inner expression that is below
263-
let needs_inner_parens = prec < ExprPrecedence::Postfix;
261+
let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Postfix);
264262
// given we are the higher precedence, no parent expression will have stronger requirements
265263
let needs_outer_parens = false;
266264
(needs_outer_parens, needs_inner_parens)
267265
} else {
268-
// We need to wrap all binary like things, thats everything below prefix except for jumps
269-
let needs_inner_parens = prec < ExprPrecedence::Prefix && prec != ExprPrecedence::Jump;
266+
let needs_inner_parens = prec.needs_parentheses_in(ExprPrecedence::Prefix);
270267
let parent = expr
271268
.syntax()
272269
.parent()
@@ -278,8 +275,8 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
278275

279276
// if we have no parent, we don't need outer parens to disambiguate
280277
// otherwise anything with higher precedence than what we insert needs to wrap us
281-
let needs_outer_parens =
282-
parent.is_some_and(|parent_prec| parent_prec > ExprPrecedence::Prefix);
278+
let needs_outer_parens = parent
279+
.is_some_and(|parent_prec| ExprPrecedence::Prefix.needs_parentheses_in(parent_prec));
283280
(needs_outer_parens, needs_inner_parens)
284281
}
285282
}

crates/syntax/src/ast/prec.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ pub enum ExprPrecedence {
4141
Unambiguous,
4242
}
4343

44+
impl ExprPrecedence {
45+
pub fn needs_parentheses_in(self, other: ExprPrecedence) -> bool {
46+
match other {
47+
ExprPrecedence::Unambiguous => false,
48+
// postfix ops have higher precedence than any other operator, so we need to wrap
49+
// any inner expression that is below
50+
ExprPrecedence::Postfix => self < ExprPrecedence::Postfix,
51+
// We need to wrap all binary like things, thats everything below prefix except for
52+
// jumps (as those are prefix operations as well)
53+
ExprPrecedence::Prefix => ExprPrecedence::Jump < self && self < ExprPrecedence::Prefix,
54+
parent => self <= parent,
55+
}
56+
}
57+
}
58+
4459
#[derive(PartialEq, Debug)]
4560
pub enum Fixity {
4661
/// The operator is left-associative
@@ -137,7 +152,7 @@ impl Expr {
137152
// - https://github.com/rust-lang/rust/blob/b6852428a8ea9728369b64b9964cad8e258403d3/compiler/rustc_ast/src/util/parser.rs#L296
138153

139154
/// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
140-
pub fn needs_parens_in(&self, parent: SyntaxNode) -> bool {
155+
pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool {
141156
match_ast! {
142157
match parent {
143158
ast::Expr(e) => self.needs_parens_in_expr(&e),

0 commit comments

Comments
 (0)