Skip to content

Commit f4fec4f

Browse files
committed
Auto merge of rust-lang#16378 - roife:fix/issue-15470, r=Veykril
fix: better handling of SelfParam in assist 'inline_call' fix rust-lang#15470. The current `inline_call` directly translates `&self` into `let ref this = ...;` and `&mut self` into `let ref mut this = ...;`. However, it does not handle some complex scenarios. This PR addresses the following transformations (assuming the receiving object is `obj`): - `self`: `let this = obj` - `mut self`: `let mut this = obj` - `&self`: `let this = &obj` - `&mut self` + If `obj` is `let mut obj = ...`, use a mutable reference: `let this = &mut obj` + If `obj` is `let obj = &mut ...;`, perform a reborrow: `let this = &mut *obj`
2 parents 0333646 + 920e99a commit f4fec4f

File tree

2 files changed

+134
-17
lines changed

2 files changed

+134
-17
lines changed

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

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ide_db::{
1515
};
1616
use itertools::{izip, Itertools};
1717
use syntax::{
18-
ast::{self, edit::IndentLevel, edit_in_place::Indent, HasArgList, PathExpr},
18+
ast::{self, edit::IndentLevel, edit_in_place::Indent, HasArgList, Pat, PathExpr},
1919
ted, AstNode, NodeOrToken, SyntaxKind,
2020
};
2121

@@ -278,7 +278,7 @@ fn get_fn_params(
278278

279279
let mut params = Vec::new();
280280
if let Some(self_param) = param_list.self_param() {
281-
// FIXME this should depend on the receiver as well as the self_param
281+
// Keep `ref` and `mut` and transform them into `&` and `mut` later
282282
params.push((
283283
make::ident_pat(
284284
self_param.amp_token().is_some(),
@@ -409,16 +409,56 @@ fn inline(
409409
let mut let_stmts = Vec::new();
410410

411411
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
412-
for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments) {
412+
for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {
413413
// izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors
414414
let usages: &[ast::PathExpr] = &usages;
415415
let expr: &ast::Expr = expr;
416416

417417
let mut insert_let_stmt = || {
418418
let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone());
419-
let_stmts.push(
420-
make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
421-
);
419+
420+
let is_self = param
421+
.name(sema.db)
422+
.and_then(|name| name.as_text())
423+
.is_some_and(|name| name == "self");
424+
425+
if is_self {
426+
let mut this_pat = make::ident_pat(false, false, make::name("this"));
427+
let mut expr = expr.clone();
428+
match pat {
429+
Pat::IdentPat(pat) => match (pat.ref_token(), pat.mut_token()) {
430+
// self => let this = obj
431+
(None, None) => {}
432+
// mut self => let mut this = obj
433+
(None, Some(_)) => {
434+
this_pat = make::ident_pat(false, true, make::name("this"));
435+
}
436+
// &self => let this = &obj
437+
(Some(_), None) => {
438+
expr = make::expr_ref(expr, false);
439+
}
440+
// let foo = &mut X; &mut self => let this = &mut obj
441+
// let mut foo = X; &mut self => let this = &mut *obj (reborrow)
442+
(Some(_), Some(_)) => {
443+
let should_reborrow = sema
444+
.type_of_expr(&expr)
445+
.map(|ty| ty.original.is_mutable_reference());
446+
expr = if let Some(true) = should_reborrow {
447+
make::expr_reborrow(expr)
448+
} else {
449+
make::expr_ref(expr, true)
450+
};
451+
}
452+
},
453+
_ => {}
454+
};
455+
let_stmts
456+
.push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into())
457+
} else {
458+
let_stmts.push(
459+
make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
460+
);
461+
}
422462
};
423463

424464
// check if there is a local var in the function that conflicts with parameter
@@ -484,12 +524,10 @@ fn inline(
484524
body = make::block_expr(let_stmts, Some(body.into())).clone_for_update();
485525
}
486526
} else if let Some(stmt_list) = body.stmt_list() {
487-
ted::insert_all(
488-
ted::Position::after(
489-
stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing."),
490-
),
491-
let_stmts.into_iter().map(|stmt| stmt.syntax().clone().into()).collect(),
492-
);
527+
let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing.");
528+
let_stmts.into_iter().rev().for_each(|let_stmt| {
529+
ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone());
530+
});
493531
}
494532

495533
let original_indentation = match node {
@@ -721,7 +759,7 @@ impl Foo {
721759
722760
fn main() {
723761
let x = {
724-
let ref this = Foo(3);
762+
let this = &Foo(3);
725763
Foo(this.0 + 2)
726764
};
727765
}
@@ -757,7 +795,7 @@ impl Foo {
757795
758796
fn main() {
759797
let x = {
760-
let ref this = Foo(3);
798+
let this = &Foo(3);
761799
Foo(this.0 + 2)
762800
};
763801
}
@@ -795,7 +833,7 @@ impl Foo {
795833
fn main() {
796834
let mut foo = Foo(3);
797835
{
798-
let ref mut this = foo;
836+
let this = &mut foo;
799837
this.0 = 0;
800838
};
801839
}
@@ -882,7 +920,7 @@ impl Foo {
882920
}
883921
fn bar(&self) {
884922
{
885-
let ref this = self;
923+
let this = &self;
886924
this;
887925
this;
888926
};
@@ -1557,7 +1595,7 @@ impl Enum {
15571595
15581596
fn a() -> bool {
15591597
{
1560-
let ref this = Enum::A;
1598+
let this = &Enum::A;
15611599
this == &Enum::A || this == &Enum::B
15621600
}
15631601
}
@@ -1619,6 +1657,82 @@ fn main() {
16191657
a as A
16201658
};
16211659
}
1660+
"#,
1661+
)
1662+
}
1663+
1664+
#[test]
1665+
fn method_by_reborrow() {
1666+
check_assist(
1667+
inline_call,
1668+
r#"
1669+
pub struct Foo(usize);
1670+
1671+
impl Foo {
1672+
fn add1(&mut self) {
1673+
self.0 += 1;
1674+
}
1675+
}
1676+
1677+
pub fn main() {
1678+
let f = &mut Foo(0);
1679+
f.add1$0();
1680+
}
1681+
"#,
1682+
r#"
1683+
pub struct Foo(usize);
1684+
1685+
impl Foo {
1686+
fn add1(&mut self) {
1687+
self.0 += 1;
1688+
}
1689+
}
1690+
1691+
pub fn main() {
1692+
let f = &mut Foo(0);
1693+
{
1694+
let this = &mut *f;
1695+
this.0 += 1;
1696+
};
1697+
}
1698+
"#,
1699+
)
1700+
}
1701+
1702+
#[test]
1703+
fn method_by_mut() {
1704+
check_assist(
1705+
inline_call,
1706+
r#"
1707+
pub struct Foo(usize);
1708+
1709+
impl Foo {
1710+
fn add1(mut self) {
1711+
self.0 += 1;
1712+
}
1713+
}
1714+
1715+
pub fn main() {
1716+
let mut f = Foo(0);
1717+
f.add1$0();
1718+
}
1719+
"#,
1720+
r#"
1721+
pub struct Foo(usize);
1722+
1723+
impl Foo {
1724+
fn add1(mut self) {
1725+
self.0 += 1;
1726+
}
1727+
}
1728+
1729+
pub fn main() {
1730+
let mut f = Foo(0);
1731+
{
1732+
let mut this = f;
1733+
this.0 += 1;
1734+
};
1735+
}
16221736
"#,
16231737
)
16241738
}

crates/syntax/src/ast/make.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,9 @@ pub fn expr_macro_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
595595
pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
596596
expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
597597
}
598+
pub fn expr_reborrow(expr: ast::Expr) -> ast::Expr {
599+
expr_from_text(&format!("&mut *{expr}"))
600+
}
598601
pub fn expr_closure(pats: impl IntoIterator<Item = ast::Param>, expr: ast::Expr) -> ast::Expr {
599602
let params = pats.into_iter().join(", ");
600603
expr_from_text(&format!("|{params}| {expr}"))

0 commit comments

Comments
 (0)