rust/crates/ra_assists/src/handlers/inline_local_variable.rs

691 lines
12 KiB
Rust
Raw Normal View History

2020-03-04 07:25:22 -06:00
use ra_ide_db::defs::Definition;
use ra_syntax::{
2019-08-19 06:11:09 -05:00
ast::{self, AstNode, AstToken},
TextRange,
};
2020-03-03 09:56:42 -06:00
use test_utils::tested_by;
2019-03-24 12:05:11 -05:00
2020-03-03 09:56:42 -06:00
use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId};
2019-03-24 12:05:11 -05:00
2019-10-26 11:08:13 -05:00
// Assist: inline_local_variable
//
// Inlines local variable.
//
// ```
// fn main() {
// let x<|> = 1 + 2;
// x * 4;
// }
// ```
// ->
// ```
// fn main() {
// (1 + 2) * 4;
// }
// ```
2020-02-06 09:58:57 -06:00
pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
2019-08-19 06:11:09 -05:00
let bind_pat = match let_stmt.pat()? {
ast::Pat::BindPat(pat) => pat,
2019-03-24 12:05:11 -05:00
_ => return None,
};
2020-04-09 16:35:05 -05:00
if bind_pat.mut_token().is_some() {
2020-03-03 09:56:42 -06:00
tested_by!(test_not_inline_mut_variable);
return None;
}
if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) {
tested_by!(not_applicable_outside_of_bind_pat);
2019-03-24 12:05:11 -05:00
return None;
}
2019-07-19 03:24:41 -05:00
let initializer_expr = let_stmt.initializer()?;
2020-03-04 07:25:22 -06:00
let def = ctx.sema.to_def(&bind_pat)?;
let def = Definition::Local(def);
let refs = def.find_usages(ctx.db, None);
if refs.is_empty() {
tested_by!(test_not_applicable_if_variable_unused);
return None;
};
2019-03-30 05:25:53 -05:00
let delete_range = if let Some(whitespace) = let_stmt
.syntax()
.next_sibling_or_token()
2019-07-19 03:24:41 -05:00
.and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
2019-03-24 12:05:11 -05:00
{
2019-07-20 04:58:27 -05:00
TextRange::from_to(
let_stmt.syntax().text_range().start(),
whitespace.syntax().text_range().end(),
)
2019-03-24 12:05:11 -05:00
} else {
2019-07-20 04:58:27 -05:00
let_stmt.syntax().text_range()
2019-03-24 12:05:11 -05:00
};
let mut wrap_in_parens = vec![true; refs.len()];
for (i, desc) in refs.iter().enumerate() {
2020-03-04 07:25:22 -06:00
let usage_node = ctx
.covering_node_for_range(desc.file_range.range)
.ancestors()
.find_map(ast::PathExpr::cast)?;
let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
let usage_parent = match usage_parent_option {
Some(u) => u,
None => {
wrap_in_parens[i] = false;
continue;
}
};
2019-08-19 06:11:09 -05:00
wrap_in_parens[i] = match (&initializer_expr, usage_parent) {
(ast::Expr::CallExpr(_), _)
| (ast::Expr::IndexExpr(_), _)
| (ast::Expr::MethodCallExpr(_), _)
| (ast::Expr::FieldExpr(_), _)
| (ast::Expr::TryExpr(_), _)
| (ast::Expr::RefExpr(_), _)
| (ast::Expr::Literal(_), _)
| (ast::Expr::TupleExpr(_), _)
| (ast::Expr::ArrayExpr(_), _)
| (ast::Expr::ParenExpr(_), _)
| (ast::Expr::PathExpr(_), _)
| (ast::Expr::BlockExpr(_), _)
| (_, ast::Expr::CallExpr(_))
| (_, ast::Expr::TupleExpr(_))
| (_, ast::Expr::ArrayExpr(_))
| (_, ast::Expr::ParenExpr(_))
| (_, ast::Expr::ForExpr(_))
| (_, ast::Expr::WhileExpr(_))
| (_, ast::Expr::BreakExpr(_))
| (_, ast::Expr::ReturnExpr(_))
| (_, ast::Expr::MatchExpr(_)) => false,
_ => true,
};
}
2019-07-19 03:24:41 -05:00
let init_str = initializer_expr.syntax().text().to_string();
let init_in_paren = format!("({})", &init_str);
ctx.add_assist(
2019-03-24 12:05:11 -05:00
AssistId("inline_local_variable"),
2020-01-14 11:32:26 -06:00
"Inline variable",
2020-01-01 17:39:01 -06:00
move |edit: &mut ActionBuilder| {
2019-03-24 12:05:11 -05:00
edit.delete(delete_range);
for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
2020-03-04 07:25:22 -06:00
let replacement =
if should_wrap { init_in_paren.clone() } else { init_str.clone() };
edit.replace(desc.file_range.range, replacement)
2019-03-24 12:05:11 -05:00
}
edit.set_cursor(delete_range.start())
},
)
2019-03-24 12:05:11 -05:00
}
#[cfg(test)]
mod tests {
2020-03-03 09:56:42 -06:00
use test_utils::covers;
2019-03-24 12:05:11 -05:00
use crate::helpers::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
fn test_inline_let_bind_literal_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
let a<|> = 1;
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
<|>1 + 1;
if 1 > 10 {
}
while 1 > 10 {
}
let b = 1 * 10;
bar(1);
}",
);
}
#[test]
fn test_inline_let_bind_bin_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
let a<|> = 1 + 1;
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
<|>(1 + 1) + 1;
if (1 + 1) > 10 {
}
while (1 + 1) > 10 {
}
let b = (1 + 1) * 10;
bar(1 + 1);
2019-03-24 12:05:11 -05:00
}",
);
}
#[test]
fn test_inline_let_bind_function_call_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
let a<|> = bar(1);
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize) {}
fn foo() {
<|>bar(1) + 1;
if bar(1) > 10 {
}
while bar(1) > 10 {
}
let b = bar(1) * 10;
bar(bar(1));
}",
);
}
#[test]
fn test_inline_let_bind_cast_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize): usize { a }
fn foo() {
let a<|> = bar(1) as u64;
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn bar(a: usize): usize { a }
fn foo() {
<|>(bar(1) as u64) + 1;
if (bar(1) as u64) > 10 {
}
while (bar(1) as u64) > 10 {
}
let b = (bar(1) as u64) * 10;
bar(bar(1) as u64);
2019-03-24 12:05:11 -05:00
}",
);
}
#[test]
fn test_inline_let_bind_block_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn foo() {
let a<|> = { 10 + 1 };
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn foo() {
<|>{ 10 + 1 } + 1;
if { 10 + 1 } > 10 {
}
while { 10 + 1 } > 10 {
}
let b = { 10 + 1 } * 10;
bar({ 10 + 1 });
}",
);
}
#[test]
fn test_inline_let_bind_paren_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn foo() {
let a<|> = ( 10 + 1 );
a + 1;
if a > 10 {
}
while a > 10 {
}
let b = a * 10;
bar(a);
}",
2020-03-03 10:03:46 -06:00
r"
2019-03-24 12:05:11 -05:00
fn foo() {
<|>( 10 + 1 ) + 1;
if ( 10 + 1 ) > 10 {
}
while ( 10 + 1 ) > 10 {
}
let b = ( 10 + 1 ) * 10;
bar(( 10 + 1 ));
}",
);
}
#[test]
fn test_not_inline_mut_variable() {
2020-03-03 09:56:42 -06:00
covers!(test_not_inline_mut_variable);
2019-03-24 12:05:11 -05:00
check_assist_not_applicable(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 09:56:42 -06:00
r"
2019-03-24 12:05:11 -05:00
fn foo() {
let mut a<|> = 1 + 1;
a + 1;
}",
);
}
#[test]
fn test_call_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = bar(10 + 1);
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = bar(10 + 1) * 10;
let c = bar(10 + 1) as usize;
}",
);
}
#[test]
fn test_index_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let x = vec![1, 2, 3];
let a<|> = x[0];
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let x = vec![1, 2, 3];
<|>let b = x[0] * 10;
let c = x[0] as usize;
}",
);
}
#[test]
fn test_method_call_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let bar = vec![1];
let a<|> = bar.len();
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let bar = vec![1];
<|>let b = bar.len() * 10;
let c = bar.len() as usize;
}",
);
}
#[test]
fn test_field_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
struct Bar {
foo: usize
}
fn foo() {
let bar = Bar { foo: 1 };
let a<|> = bar.foo;
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
struct Bar {
foo: usize
}
fn foo() {
let bar = Bar { foo: 1 };
<|>let b = bar.foo * 10;
let c = bar.foo as usize;
}",
);
}
#[test]
fn test_try_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() -> Option<usize> {
let bar = Some(1);
let a<|> = bar?;
let b = a * 10;
let c = a as usize;
None
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() -> Option<usize> {
let bar = Some(1);
<|>let b = bar? * 10;
let c = bar? as usize;
None
}",
);
}
#[test]
fn test_ref_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let bar = 10;
let a<|> = &bar;
let b = a * 10;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let bar = 10;
<|>let b = &bar * 10;
}",
);
}
#[test]
fn test_tuple_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = (10, 20);
let b = a[0];
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = (10, 20)[0];
}",
);
}
#[test]
fn test_array_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = [1, 2, 3];
let b = a.len();
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = [1, 2, 3].len();
}",
);
}
#[test]
fn test_paren() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = (10 + 20);
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = (10 + 20) * 10;
let c = (10 + 20) as usize;
}",
);
}
#[test]
fn test_path_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let d = 10;
let a<|> = d;
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let d = 10;
<|>let b = d * 10;
let c = d as usize;
}",
);
}
#[test]
fn test_block_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = { 10 };
let b = a * 10;
let c = a as usize;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = { 10 } * 10;
let c = { 10 } as usize;
}",
);
}
#[test]
fn test_used_in_different_expr1() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = 10 + 20;
let b = a * 10;
let c = (a, 20);
let d = [a, 10];
let e = (a);
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>let b = (10 + 20) * 10;
let c = (10 + 20, 20);
let d = [10 + 20, 10];
let e = (10 + 20);
}",
);
}
#[test]
fn test_used_in_for_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = vec![10, 20];
for i in a {}
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>for i in vec![10, 20] {}
}",
);
}
#[test]
fn test_used_in_while_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = 1 > 0;
while a {}
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>while 1 > 0 {}
}",
);
}
#[test]
fn test_used_in_break_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = 1 + 1;
loop {
break a;
}
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>loop {
break 1 + 1;
}
}",
);
}
#[test]
fn test_used_in_return_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = 1 > 0;
return a;
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>return 1 > 0;
}",
);
}
#[test]
fn test_used_in_match_expr() {
check_assist(
2020-01-19 10:39:53 -06:00
inline_local_variable,
2020-03-03 10:03:46 -06:00
r"
fn foo() {
let a<|> = 1 > 0;
match a {}
}",
2020-03-03 10:03:46 -06:00
r"
fn foo() {
<|>match 1 > 0 {}
2019-03-24 12:05:11 -05:00
}",
);
}
2020-01-25 14:07:21 -06:00
#[test]
fn test_not_applicable_if_variable_unused() {
2020-03-04 07:25:22 -06:00
covers!(test_not_applicable_if_variable_unused);
2020-01-25 14:07:21 -06:00
check_assist_not_applicable(
inline_local_variable,
2020-03-03 09:56:42 -06:00
r"
2020-01-25 14:07:21 -06:00
fn foo() {
let <|>a = 0;
}
",
)
}
2020-03-03 09:56:42 -06:00
#[test]
fn not_applicable_outside_of_bind_pat() {
covers!(not_applicable_outside_of_bind_pat);
check_assist_not_applicable(
inline_local_variable,
r"
fn main() {
let x = <|>1 + 2;
x * 4;
}
",
)
}
2019-03-24 12:05:11 -05:00
}