**Remove Unused Parameter** refactoring

This commit is contained in:
Aleksey Kladov 2020-08-19 18:44:33 +02:00
parent f5b7540f38
commit 4b5b55f6f3
7 changed files with 163 additions and 7 deletions

View file

@ -8,6 +8,7 @@ use syntax::{
use crate::{ use crate::{
assist_context::{AssistContext, Assists}, assist_context::{AssistContext, Assists},
utils::next_prev,
AssistId, AssistKind, AssistId, AssistKind,
}; };
@ -66,10 +67,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
) )
} }
fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}
fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTree> { fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTree> {
let lhs_path = old.path()?; let lhs_path = old.path()?;
let rhs_path = new.path()?; let rhs_path = new.path()?;

View file

@ -82,9 +82,10 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
use super::*;
#[test] #[test]
fn test_remove_dbg() { fn test_remove_dbg() {
check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1"); check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

View file

@ -0,0 +1,131 @@
use ide_db::{defs::Definition, search::Reference};
use syntax::{
algo::find_node_at_range,
ast::{self, ArgListOwner},
AstNode, SyntaxNode, TextRange, T,
};
use test_utils::mark;
use crate::{
assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
};
// Assist: remove_unused_param
//
// Removes unused function parameter.
//
// ```
// fn frobnicate(x: i32<|>) {}
//
// fn main() {
// frobnicate(92);
// }
// ```
// ->
// ```
// fn frobnicate() {}
//
// fn main() {
// frobnicate();
// }
// ```
pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let param: ast::Param = ctx.find_node_at_offset()?;
let ident_pat = match param.pat()? {
ast::Pat::IdentPat(it) => it,
_ => return None,
};
let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
let param_position = func.param_list()?.params().position(|it| it == param)?;
let fn_def = {
let func = ctx.sema.to_def(&func)?;
Definition::ModuleDef(func.into())
};
let param_def = {
let local = ctx.sema.to_def(&ident_pat)?;
Definition::Local(local)
};
if param_def.usages(&ctx.sema).at_least_one() {
mark::hit!(keep_used);
return None;
}
acc.add(
AssistId("remove_unused_param", AssistKind::Refactor),
"Remove unused parameter",
param.syntax().text_range(),
|builder| {
builder.delete(range_with_coma(param.syntax()));
for usage in fn_def.usages(&ctx.sema).all() {
process_usage(ctx, builder, usage, param_position);
}
},
)
}
fn process_usage(
ctx: &AssistContext,
builder: &mut AssistBuilder,
usage: Reference,
arg_to_remove: usize,
) -> Option<()> {
let source_file = ctx.sema.parse(usage.file_range.file_id);
let call_expr: ast::CallExpr =
find_node_at_range(source_file.syntax(), usage.file_range.range)?;
if call_expr.expr()?.syntax().text_range() != usage.file_range.range {
return None;
}
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
builder.edit_file(usage.file_range.file_id);
builder.delete(range_with_coma(arg.syntax()));
Some(())
}
fn range_with_coma(node: &SyntaxNode) -> TextRange {
let up_to = next_prev().find_map(|dir| {
node.siblings_with_tokens(dir)
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![,])
});
let up_to = up_to.map_or(node.text_range(), |it| it.text_range());
node.text_range().cover(up_to)
}
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
fn remove_unused() {
check_assist(
remove_unused_param,
r#"
fn a() { foo(9, 2) }
fn foo(x: i32, <|>y: i32) { x; }
fn b() { foo(9, 2,) }
"#,
r#"
fn a() { foo(9) }
fn foo(x: i32) { x; }
fn b() { foo(9, ) }
"#,
);
}
#[test]
fn keep_used() {
mark::check!(keep_used);
check_assist_not_applicable(
remove_unused_param,
r#"
fn foo(x: i32, <|>y: i32) { y; }
fn main() { foo(9, 2) }
"#,
);
}
}

View file

@ -152,6 +152,7 @@ mod handlers {
mod raw_string; mod raw_string;
mod remove_dbg; mod remove_dbg;
mod remove_mut; mod remove_mut;
mod remove_unused_param;
mod reorder_fields; mod reorder_fields;
mod replace_if_let_with_match; mod replace_if_let_with_match;
mod replace_let_with_if_let; mod replace_let_with_if_let;
@ -198,6 +199,7 @@ mod handlers {
raw_string::remove_hash, raw_string::remove_hash,
remove_dbg::remove_dbg, remove_dbg::remove_dbg,
remove_mut::remove_mut, remove_mut::remove_mut,
remove_unused_param::remove_unused_param,
reorder_fields::reorder_fields, reorder_fields::reorder_fields,
replace_if_let_with_match::replace_if_let_with_match, replace_if_let_with_match::replace_if_let_with_match,
replace_let_with_if_let::replace_let_with_if_let, replace_let_with_if_let::replace_let_with_if_let,

View file

@ -750,6 +750,27 @@ impl Walrus {
) )
} }
#[test]
fn doctest_remove_unused_param() {
check_doc_test(
"remove_unused_param",
r#####"
fn frobnicate(x: i32<|>) {}
fn main() {
frobnicate(92);
}
"#####,
r#####"
fn frobnicate() {}
fn main() {
frobnicate();
}
"#####,
)
}
#[test] #[test]
fn doctest_reorder_fields() { fn doctest_reorder_fields() {
check_doc_test( check_doc_test(

View file

@ -9,7 +9,7 @@ use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
ast::{self, make, NameOwner}, ast::{self, make, NameOwner},
AstNode, AstNode, Direction,
SyntaxKind::*, SyntaxKind::*,
SyntaxNode, TextSize, T, SyntaxNode, TextSize, T,
}; };
@ -311,3 +311,7 @@ pub use prelude::*;
Some(def) Some(def)
} }
} }
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}

View file

@ -203,7 +203,7 @@ impl<'a> FindUsages<'a> {
} }
pub fn at_least_one(self) -> bool { pub fn at_least_one(self) -> bool {
self.all().is_empty() !self.all().is_empty()
} }
pub fn all(self) -> Vec<Reference> { pub fn all(self) -> Vec<Reference> {