mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-10 23:54:58 +00:00
Merge #9474
9474: fix: Inline parameters in `inline_call` if possible r=Veykril a=Veykril Fixes #9491 Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
2bc4f9e371
@ -1,5 +1,7 @@
|
|||||||
use ast::make;
|
use ast::make;
|
||||||
use hir::{HasSource, PathResolution};
|
use hir::{HasSource, PathResolution};
|
||||||
|
use ide_db::{defs::Definition, search::FileReference};
|
||||||
|
use itertools::izip;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::AstNodeEdit, ArgListOwner},
|
ast::{self, edit::AstNodeEdit, ArgListOwner},
|
||||||
ted, AstNode,
|
ted, AstNode,
|
||||||
@ -15,19 +17,22 @@ use crate::{
|
|||||||
// Inlines a function or method body.
|
// Inlines a function or method body.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
// fn add(a: u32, b: u32) -> u32 { a + b }
|
// fn align(a: u32, b: u32) -> u32 {
|
||||||
|
// (a + b - 1) & !(b - 1)
|
||||||
|
// }
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// let x = add$0(1, 2);
|
// let x = align$0(1, 2);
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
// ->
|
// ->
|
||||||
// ```
|
// ```
|
||||||
// fn add(a: u32, b: u32) -> u32 { a + b }
|
// fn align(a: u32, b: u32) -> u32 {
|
||||||
|
// (a + b - 1) & !(b - 1)
|
||||||
|
// }
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// let x = {
|
// let x = {
|
||||||
// let a = 1;
|
|
||||||
// let b = 2;
|
// let b = 2;
|
||||||
// a + b
|
// (1 + b - 1) & !(b - 1)
|
||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
@ -69,23 +74,25 @@ pub(crate) fn inline_(
|
|||||||
arg_list: Vec<ast::Expr>,
|
arg_list: Vec<ast::Expr>,
|
||||||
expr: ast::Expr,
|
expr: ast::Expr,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let hir::InFile { value: function_source, .. } = function.source(ctx.db())?;
|
let hir::InFile { value: function_source, file_id } = function.source(ctx.db())?;
|
||||||
let param_list = function_source.param_list()?;
|
let param_list = function_source.param_list()?;
|
||||||
|
let mut assoc_fn_params = function.assoc_fn_params(ctx.sema.db).into_iter();
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
if let Some(self_param) = param_list.self_param() {
|
if let Some(self_param) = param_list.self_param() {
|
||||||
// FIXME this should depend on the receiver as well as the self_param
|
// FIXME this should depend on the receiver as well as the self_param
|
||||||
params.push(
|
params.push((
|
||||||
make::ident_pat(
|
make::ident_pat(
|
||||||
self_param.amp_token().is_some(),
|
self_param.amp_token().is_some(),
|
||||||
self_param.mut_token().is_some(),
|
self_param.mut_token().is_some(),
|
||||||
make::name("this"),
|
make::name("this"),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
assoc_fn_params.next()?,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
for param in param_list.params() {
|
for param in param_list.params() {
|
||||||
params.push(param.pat()?);
|
params.push((param.pat()?, assoc_fn_params.next()?));
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg_list.len() != params.len() {
|
if arg_list.len() != params.len() {
|
||||||
@ -95,8 +102,6 @@ pub(crate) fn inline_(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_bindings = params.into_iter().zip(arg_list);
|
|
||||||
|
|
||||||
let body = function_source.body()?;
|
let body = function_source.body()?;
|
||||||
|
|
||||||
acc.add(
|
acc.add(
|
||||||
@ -104,32 +109,87 @@ pub(crate) fn inline_(
|
|||||||
label,
|
label,
|
||||||
expr.syntax().text_range(),
|
expr.syntax().text_range(),
|
||||||
|builder| {
|
|builder| {
|
||||||
// FIXME: emit type ascriptions when a coercion happens?
|
let body = body.clone_for_update();
|
||||||
// FIXME: dont create locals when its not required
|
|
||||||
let statements = new_bindings
|
let file_id = file_id.original_file(ctx.sema.db);
|
||||||
.map(|(pattern, value)| make::let_stmt(pattern, Some(value)).into())
|
let usages_for_locals = |local| {
|
||||||
.chain(body.statements());
|
Definition::Local(local)
|
||||||
|
.usages(&ctx.sema)
|
||||||
|
.all()
|
||||||
|
.references
|
||||||
|
.remove(&file_id)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
};
|
||||||
|
// Contains the nodes of usages of parameters.
|
||||||
|
// If the inner Vec for a parameter is empty it either means there are no usages or that the parameter
|
||||||
|
// has a pattern that does not allow inlining
|
||||||
|
let param_use_nodes: Vec<Vec<_>> = params
|
||||||
|
.iter()
|
||||||
|
.map(|(pat, param)| {
|
||||||
|
if !matches!(pat, ast::Pat::IdentPat(pat) if pat.is_simple_ident()) {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
usages_for_locals(param.as_local(ctx.sema.db))
|
||||||
|
.map(|FileReference { name, range, .. }| match name {
|
||||||
|
ast::NameLike::NameRef(_) => body
|
||||||
|
.syntax()
|
||||||
|
.covering_element(range)
|
||||||
|
.ancestors()
|
||||||
|
.nth(3)
|
||||||
|
.filter(|it| ast::PathExpr::can_cast(it.kind())),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Option<Vec<_>>>()
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Rewrite `self` to `this`
|
||||||
|
if param_list.self_param().is_some() {
|
||||||
|
let this = || make::name_ref("this").syntax().clone_for_update();
|
||||||
|
usages_for_locals(params[0].1.as_local(ctx.sema.db))
|
||||||
|
.flat_map(|FileReference { name, range, .. }| match name {
|
||||||
|
ast::NameLike::NameRef(_) => Some(body.syntax().covering_element(range)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.for_each(|it| {
|
||||||
|
ted::replace(it, &this());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
|
||||||
|
for ((pat, _), usages, expr) in izip!(params, param_use_nodes, arg_list).rev() {
|
||||||
|
match &*usages {
|
||||||
|
// inline single use parameters
|
||||||
|
[usage] => {
|
||||||
|
ted::replace(usage, expr.syntax().clone_for_update());
|
||||||
|
}
|
||||||
|
// inline parameters whose expression is a simple local reference
|
||||||
|
[_, ..]
|
||||||
|
if matches!(&expr,
|
||||||
|
ast::Expr::PathExpr(expr)
|
||||||
|
if expr.path().and_then(|path| path.as_single_name_ref()).is_some()
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
usages.into_iter().for_each(|usage| {
|
||||||
|
ted::replace(usage, &expr.syntax().clone_for_update());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// cant inline, emit a let statement
|
||||||
|
// FIXME: emit type ascriptions when a coercion happens?
|
||||||
|
_ => body.push_front(make::let_stmt(pat, Some(expr)).clone_for_update().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let original_indentation = expr.indent_level();
|
let original_indentation = expr.indent_level();
|
||||||
let mut replacement = make::block_expr(statements, body.tail_expr())
|
let replacement = body.reset_indent().indent(original_indentation);
|
||||||
.reset_indent()
|
|
||||||
.indent(original_indentation);
|
|
||||||
|
|
||||||
if param_list.self_param().is_some() {
|
let replacement = match replacement.tail_expr() {
|
||||||
replacement = replacement.clone_for_update();
|
Some(expr) if replacement.statements().next().is_none() => expr,
|
||||||
let this = make::name_ref("this").syntax().clone_for_update();
|
_ => ast::Expr::BlockExpr(replacement),
|
||||||
// FIXME dont look into descendant methods
|
};
|
||||||
replacement
|
builder.replace_ast(expr, replacement);
|
||||||
.syntax()
|
|
||||||
.descendants()
|
|
||||||
.filter_map(ast::NameRef::cast)
|
|
||||||
.filter(|n| n.self_token().is_some())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.for_each(|self_ref| ted::replace(self_ref.syntax(), &this));
|
|
||||||
}
|
|
||||||
builder.replace_ast(expr, ast::Expr::BlockExpr(replacement));
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -153,31 +213,7 @@ fn main() {
|
|||||||
r#"
|
r#"
|
||||||
fn foo() { println!("Hello, World!"); }
|
fn foo() { println!("Hello, World!"); }
|
||||||
fn main() {
|
fn main() {
|
||||||
{
|
{ println!("Hello, World!"); };
|
||||||
println!("Hello, World!");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn args_with_side_effects() {
|
|
||||||
check_assist(
|
|
||||||
inline_call,
|
|
||||||
r#"
|
|
||||||
fn foo(name: String) { println!("Hello, {}!", name); }
|
|
||||||
fn main() {
|
|
||||||
foo$0(String::from("Michael"));
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
r#"
|
|
||||||
fn foo(name: String) { println!("Hello, {}!", name); }
|
|
||||||
fn main() {
|
|
||||||
{
|
|
||||||
let name = String::from("Michael");
|
|
||||||
println!("Hello, {}!", name);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
@ -195,6 +231,32 @@ fn main() { let x = add$0(42); }
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn args_with_side_effects() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
fn foo(name: String) {
|
||||||
|
println!("Hello, {}!", name);
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
foo$0(String::from("Michael"));
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(name: String) {
|
||||||
|
println!("Hello, {}!", name);
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
{
|
||||||
|
let name = String::from("Michael");
|
||||||
|
println!("Hello, {}!", name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_with_multiple_statements() {
|
fn function_with_multiple_statements() {
|
||||||
check_assist(
|
check_assist(
|
||||||
@ -219,9 +281,8 @@ fn foo(a: u32, b: u32) -> u32 {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = {
|
let x = {
|
||||||
let a = 1;
|
|
||||||
let b = 2;
|
let b = 2;
|
||||||
let x = a + b;
|
let x = 1 + b;
|
||||||
let y = x - b;
|
let y = x - b;
|
||||||
x * y
|
x * y
|
||||||
};
|
};
|
||||||
@ -257,11 +318,7 @@ impl Foo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = {
|
let x = Foo(Foo(3).0 + 2);
|
||||||
let this = Foo(3);
|
|
||||||
let a = 2;
|
|
||||||
Foo(this.0 + a)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
@ -294,11 +351,7 @@ impl Foo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = {
|
let x = Foo(Foo(3).0 + 2);
|
||||||
let this = Foo(3);
|
|
||||||
let a = 2;
|
|
||||||
Foo(this.0 + a)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
@ -333,8 +386,7 @@ impl Foo {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let x = {
|
let x = {
|
||||||
let ref this = Foo(3);
|
let ref this = Foo(3);
|
||||||
let a = 2;
|
Foo(this.0 + 2)
|
||||||
Foo(this.0 + a)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
@ -375,6 +427,119 @@ fn main() {
|
|||||||
this.0 = 0;
|
this.0 = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_single_use_expr_in_param() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
fn double(x: u32) -> u32 {
|
||||||
|
2 * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let x = 51;
|
||||||
|
let x = double$0(10 + x);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn double(x: u32) -> u32 {
|
||||||
|
2 * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let x = 51;
|
||||||
|
let x = 2 * 10 + x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_multi_use_expr_in_param() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
fn square(x: u32) -> u32 {
|
||||||
|
x * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let x = 51;
|
||||||
|
let y = square$0(10 + x);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn square(x: u32) -> u32 {
|
||||||
|
x * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let x = 51;
|
||||||
|
let y = {
|
||||||
|
let x = 10 + x;
|
||||||
|
x * x
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_multi_use_local_in_param() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
fn square(x: u32) -> u32 {
|
||||||
|
x * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let local = 51;
|
||||||
|
let y = square$0(local);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn square(x: u32) -> u32 {
|
||||||
|
x * x
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let local = 51;
|
||||||
|
let y = local * local;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn method_in_impl() {
|
||||||
|
check_assist(
|
||||||
|
inline_call,
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(&self) {
|
||||||
|
self;
|
||||||
|
self;
|
||||||
|
}
|
||||||
|
fn bar(&self) {
|
||||||
|
self.foo$0();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(&self) {
|
||||||
|
self;
|
||||||
|
self;
|
||||||
|
}
|
||||||
|
fn bar(&self) {
|
||||||
|
{
|
||||||
|
let ref this = self;
|
||||||
|
this;
|
||||||
|
this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -923,18 +923,21 @@ fn doctest_inline_call() {
|
|||||||
check_doc_test(
|
check_doc_test(
|
||||||
"inline_call",
|
"inline_call",
|
||||||
r#####"
|
r#####"
|
||||||
fn add(a: u32, b: u32) -> u32 { a + b }
|
fn align(a: u32, b: u32) -> u32 {
|
||||||
|
(a + b - 1) & !(b - 1)
|
||||||
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = add$0(1, 2);
|
let x = align$0(1, 2);
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
r#####"
|
r#####"
|
||||||
fn add(a: u32, b: u32) -> u32 { a + b }
|
fn align(a: u32, b: u32) -> u32 {
|
||||||
|
(a + b - 1) & !(b - 1)
|
||||||
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = {
|
let x = {
|
||||||
let a = 1;
|
|
||||||
let b = 2;
|
let b = 2;
|
||||||
a + b
|
(1 + b - 1) & !(b - 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
|
@ -431,6 +431,12 @@ impl ast::RecordExprFieldList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ast::BlockExpr {
|
||||||
|
pub fn push_front(&self, statement: ast::Stmt) {
|
||||||
|
ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
||||||
let l = node
|
let l = node
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
|
@ -641,6 +641,15 @@ impl ast::SlicePat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ast::IdentPat {
|
||||||
|
pub fn is_simple_ident(&self) -> bool {
|
||||||
|
self.at_token().is_none()
|
||||||
|
&& self.mut_token().is_none()
|
||||||
|
&& self.ref_token().is_none()
|
||||||
|
&& self.pat().is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum SelfParamKind {
|
pub enum SelfParamKind {
|
||||||
/// self
|
/// self
|
||||||
|
@ -187,6 +187,12 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if left.kind() == T!['{'] && right.kind() == SyntaxKind::LET_STMT {
|
||||||
|
let mut indent = IndentLevel::from_element(left);
|
||||||
|
indent.0 += 1;
|
||||||
|
return Some(make::tokens::whitespace(&format!("\n{}", indent)));
|
||||||
|
}
|
||||||
|
|
||||||
if right.kind() == SyntaxKind::USE {
|
if right.kind() == SyntaxKind::USE {
|
||||||
let mut indent = IndentLevel::from_element(left);
|
let mut indent = IndentLevel::from_element(left);
|
||||||
if left.kind() == SyntaxKind::USE {
|
if left.kind() == SyntaxKind::USE {
|
||||||
|
Loading…
Reference in New Issue
Block a user