Skip to content

Commit 60da021

Browse files
authored
Merge pull request #19324 from ShoyuVanilla/migrate-inline-var
fix: Prevent wrong invocations of `needs_parens_in` with non-ancestral "parent"s
2 parents d11c5b8 + 965a0c0 commit 60da021

File tree

5 files changed

+174
-48
lines changed

5 files changed

+174
-48
lines changed

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
128128
let parent = neg_expr.syntax().parent();
129129
editor = builder.make_editor(neg_expr.syntax());
130130

131-
if parent.is_some_and(|parent| demorganed.needs_parens_in(&parent)) {
131+
if parent.is_some_and(|parent| {
132+
demorganed.needs_parens_in_place_of(&parent, neg_expr.syntax())
133+
}) {
132134
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
133135
editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
134136
} else {
@@ -392,15 +394,19 @@ fn f() { !(S <= S || S < S) }
392394

393395
#[test]
394396
fn demorgan_keep_pars_for_op_precedence3() {
395-
check_assist(apply_demorgan, "fn f() { (a || !(b &&$0 c); }", "fn f() { (a || !b || !c; }");
397+
check_assist(
398+
apply_demorgan,
399+
"fn f() { (a || !(b &&$0 c); }",
400+
"fn f() { (a || (!b || !c); }",
401+
);
396402
}
397403

398404
#[test]
399-
fn demorgan_removes_pars_in_eq_precedence() {
405+
fn demorgan_keeps_pars_in_eq_precedence() {
400406
check_assist(
401407
apply_demorgan,
402408
"fn() { let x = a && !(!b |$0| !c); }",
403-
"fn() { let x = a && b && c; }",
409+
"fn() { let x = a && (b && c); }",
404410
)
405411
}
406412

Diff for: src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs

+85-33
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ide_db::{
55
EditionedFileId, RootDatabase,
66
};
77
use syntax::{
8-
ast::{self, AstNode, AstToken, HasName},
8+
ast::{self, syntax_factory::SyntaxFactory, AstNode, AstToken, HasName},
99
SyntaxElement, TextRange,
1010
};
1111

@@ -43,22 +43,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
4343
}?;
4444
let initializer_expr = let_stmt.initializer()?;
4545

46-
let delete_range = delete_let.then(|| {
47-
if let Some(whitespace) = let_stmt
48-
.syntax()
49-
.next_sibling_or_token()
50-
.and_then(SyntaxElement::into_token)
51-
.and_then(ast::Whitespace::cast)
52-
{
53-
TextRange::new(
54-
let_stmt.syntax().text_range().start(),
55-
whitespace.syntax().text_range().end(),
56-
)
57-
} else {
58-
let_stmt.syntax().text_range()
59-
}
60-
});
61-
6246
let wrap_in_parens = references
6347
.into_iter()
6448
.filter_map(|FileReference { range, name, .. }| match name {
@@ -73,40 +57,60 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
7357
}
7458
let usage_node =
7559
name_ref.syntax().ancestors().find(|it| ast::PathExpr::can_cast(it.kind()));
76-
let usage_parent_option = usage_node.and_then(|it| it.parent());
60+
let usage_parent_option = usage_node.as_ref().and_then(|it| it.parent());
7761
let usage_parent = match usage_parent_option {
7862
Some(u) => u,
79-
None => return Some((range, name_ref, false)),
63+
None => return Some((name_ref, false)),
8064
};
81-
Some((range, name_ref, initializer_expr.needs_parens_in(&usage_parent)))
65+
let should_wrap = initializer_expr
66+
.needs_parens_in_place_of(&usage_parent, usage_node.as_ref().unwrap());
67+
Some((name_ref, should_wrap))
8268
})
8369
.collect::<Option<Vec<_>>>()?;
8470

85-
let init_str = initializer_expr.syntax().text().to_string();
86-
let init_in_paren = format!("({init_str})");
87-
8871
let target = match target {
89-
ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
90-
ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
72+
ast::NameOrNameRef::Name(it) => it.syntax().clone(),
73+
ast::NameOrNameRef::NameRef(it) => it.syntax().clone(),
9174
};
9275

9376
acc.add(
9477
AssistId("inline_local_variable", AssistKind::RefactorInline),
9578
"Inline variable",
96-
target,
79+
target.text_range(),
9780
move |builder| {
98-
if let Some(range) = delete_range {
99-
builder.delete(range);
81+
let mut editor = builder.make_editor(&target);
82+
if delete_let {
83+
editor.delete(let_stmt.syntax());
84+
if let Some(whitespace) = let_stmt
85+
.syntax()
86+
.next_sibling_or_token()
87+
.and_then(SyntaxElement::into_token)
88+
.and_then(ast::Whitespace::cast)
89+
{
90+
editor.delete(whitespace.syntax());
91+
}
10092
}
101-
for (range, name, should_wrap) in wrap_in_parens {
102-
let replacement = if should_wrap { &init_in_paren } else { &init_str };
103-
if ast::RecordExprField::for_field_name(&name).is_some() {
93+
94+
let make = SyntaxFactory::new();
95+
96+
for (name, should_wrap) in wrap_in_parens {
97+
let replacement = if should_wrap {
98+
make.expr_paren(initializer_expr.clone()).into()
99+
} else {
100+
initializer_expr.clone()
101+
};
102+
103+
if let Some(record_field) = ast::RecordExprField::for_field_name(&name) {
104104
cov_mark::hit!(inline_field_shorthand);
105-
builder.insert(range.end(), format!(": {replacement}"));
105+
let replacement = make.record_expr_field(name, Some(replacement));
106+
editor.replace(record_field.syntax(), replacement.syntax());
106107
} else {
107-
builder.replace(range, replacement.clone())
108+
editor.replace(name.syntax(), replacement.syntax());
108109
}
109110
}
111+
112+
editor.add_mappings(make.finish_with_mappings());
113+
builder.add_file_edits(ctx.file_id(), editor);
110114
},
111115
)
112116
}
@@ -939,6 +943,54 @@ fn main() {
939943
fn main() {
940944
let _ = (|| 2)();
941945
}
946+
"#,
947+
);
948+
}
949+
950+
#[test]
951+
fn test_wrap_in_parens() {
952+
check_assist(
953+
inline_local_variable,
954+
r#"
955+
fn main() {
956+
let $0a = 123 < 456;
957+
let b = !a;
958+
}
959+
"#,
960+
r#"
961+
fn main() {
962+
let b = !(123 < 456);
963+
}
964+
"#,
965+
);
966+
check_assist(
967+
inline_local_variable,
968+
r#"
969+
trait Foo {
970+
fn foo(&self);
971+
}
972+
973+
impl Foo for bool {
974+
fn foo(&self) {}
975+
}
976+
977+
fn main() {
978+
let $0a = 123 < 456;
979+
let b = a.foo();
980+
}
981+
"#,
982+
r#"
983+
trait Foo {
984+
fn foo(&self);
985+
}
986+
987+
impl Foo for bool {
988+
fn foo(&self) {}
989+
}
990+
991+
fn main() {
992+
let b = (123 < 456).foo();
993+
}
942994
"#,
943995
);
944996
}

Diff for: src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs

+55-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Precedence representation.
22
3+
use stdx::always;
4+
35
use crate::{
46
ast::{self, BinaryOp, Expr, HasArgList, RangeItem},
57
match_ast, AstNode, SyntaxNode,
@@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
140142
}
141143
}
142144

145+
fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool {
146+
let bail = || always!(false, "{} is not an ancestor of {}", ancestor, descendent);
147+
148+
if !ancestor.text_range().contains_range(descendent.text_range()) {
149+
return bail();
150+
}
151+
152+
for anc in descendent.ancestors() {
153+
if anc == *ancestor {
154+
return true;
155+
}
156+
}
157+
158+
bail()
159+
}
160+
143161
impl Expr {
144162
pub fn precedence(&self) -> ExprPrecedence {
145163
precedence(self)
@@ -153,9 +171,19 @@ impl Expr {
153171

154172
/// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
155173
pub fn needs_parens_in(&self, parent: &SyntaxNode) -> bool {
174+
self.needs_parens_in_place_of(parent, self.syntax())
175+
}
176+
177+
/// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of`
178+
/// given that `place_of`'s parent is `parent`.
179+
pub fn needs_parens_in_place_of(&self, parent: &SyntaxNode, place_of: &SyntaxNode) -> bool {
180+
if !check_ancestry(parent, place_of) {
181+
return false;
182+
}
183+
156184
match_ast! {
157185
match parent {
158-
ast::Expr(e) => self.needs_parens_in_expr(&e),
186+
ast::Expr(e) => self.needs_parens_in_expr(&e, place_of),
159187
ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)),
160188
ast::StmtList(_) => self.needs_parens_in_stmt(None),
161189
ast::ArgList(_) => false,
@@ -165,7 +193,7 @@ impl Expr {
165193
}
166194
}
167195

168-
fn needs_parens_in_expr(&self, parent: &Expr) -> bool {
196+
fn needs_parens_in_expr(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
169197
// Parentheses are necessary when calling a function-like pointer that is a member of a struct or union
170198
// (e.g. `(a.f)()`).
171199
let is_parent_call_expr = matches!(parent, ast::Expr::CallExpr(_));
@@ -199,13 +227,17 @@ impl Expr {
199227

200228
if self.is_paren_like()
201229
|| parent.is_paren_like()
202-
|| self.is_prefix() && (parent.is_prefix() || !self.is_ordered_before(parent))
203-
|| self.is_postfix() && (parent.is_postfix() || self.is_ordered_before(parent))
230+
|| self.is_prefix()
231+
&& (parent.is_prefix()
232+
|| !self.is_ordered_before_parent_in_place_of(parent, place_of))
233+
|| self.is_postfix()
234+
&& (parent.is_postfix()
235+
|| self.is_ordered_before_parent_in_place_of(parent, place_of))
204236
{
205237
return false;
206238
}
207239

208-
let (left, right, inv) = match self.is_ordered_before(parent) {
240+
let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) {
209241
true => (self, parent, false),
210242
false => (parent, self, true),
211243
};
@@ -413,13 +445,28 @@ impl Expr {
413445
}
414446
}
415447

416-
fn is_ordered_before(&self, other: &Expr) -> bool {
448+
fn is_ordered_before_parent_in_place_of(&self, parent: &Expr, place_of: &SyntaxNode) -> bool {
449+
use rowan::TextSize;
417450
use Expr::*;
418451

419-
return order(self) < order(other);
452+
let self_range = self.syntax().text_range();
453+
let place_of_range = place_of.text_range();
454+
455+
let self_order_adjusted = order(self) - self_range.start() + place_of_range.start();
456+
457+
let parent_order = order(parent);
458+
let parent_order_adjusted = if parent_order <= place_of_range.start() {
459+
parent_order
460+
} else if parent_order >= place_of_range.end() {
461+
parent_order - place_of_range.len() + self_range.len()
462+
} else {
463+
return false;
464+
};
465+
466+
return self_order_adjusted < parent_order_adjusted;
420467

421468
/// Returns text range that can be used to compare two expression for order (which goes first).
422-
fn order(this: &Expr) -> rowan::TextSize {
469+
fn order(this: &Expr) -> TextSize {
423470
// For non-paren-like operators: get the operator itself
424471
let token = match this {
425472
RangeExpr(e) => e.op_token(),

Diff for: src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs

+21
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,27 @@ impl SyntaxFactory {
783783
ast
784784
}
785785

786+
pub fn record_expr_field(
787+
&self,
788+
name: ast::NameRef,
789+
expr: Option<ast::Expr>,
790+
) -> ast::RecordExprField {
791+
let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update();
792+
793+
if let Some(mut mapping) = self.mappings() {
794+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
795+
796+
builder.map_node(name.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
797+
if let Some(expr) = expr {
798+
builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
799+
}
800+
801+
builder.finish(&mut mapping);
802+
}
803+
804+
ast
805+
}
806+
786807
pub fn record_field_list(
787808
&self,
788809
fields: impl IntoIterator<Item = ast::RecordField>,

Diff for: src/tools/rust-analyzer/docs/book/src/assists_generated.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ fn main() {
306306

307307

308308
### `apply_demorgan_iterator`
309-
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L154)
309+
**Source:** [apply_demorgan.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/apply_demorgan.rs#L156)
310310

311311
Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to
312312
`Iterator::all` and `Iterator::any`.
@@ -1070,7 +1070,7 @@ pub use foo::{Bar, Baz};
10701070

10711071

10721072
### `expand_record_rest_pattern`
1073-
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L24)
1073+
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L26)
10741074

10751075
Fills fields by replacing rest pattern in record patterns.
10761076

@@ -1094,7 +1094,7 @@ fn foo(bar: Bar) {
10941094

10951095

10961096
### `expand_tuple_struct_rest_pattern`
1097-
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L80)
1097+
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L82)
10981098

10991099
Fills fields by replacing rest pattern in tuple struct patterns.
11001100

0 commit comments

Comments
 (0)