2021-01-12 08:51:02 -06:00
|
|
|
use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
|
2020-08-19 11:44:33 -05:00
|
|
|
use syntax::{
|
|
|
|
algo::find_node_at_range,
|
|
|
|
ast::{self, ArgListOwner},
|
2021-01-11 17:05:07 -06:00
|
|
|
AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
|
2020-08-19 11:44:33 -05:00
|
|
|
};
|
|
|
|
use test_utils::mark;
|
2020-12-05 07:28:15 -06:00
|
|
|
use SyntaxKind::WHITESPACE;
|
2020-08-19 11:44:33 -05:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Assist: remove_unused_param
|
|
|
|
//
|
|
|
|
// Removes unused function parameter.
|
|
|
|
//
|
|
|
|
// ```
|
2021-01-06 14:15:48 -06:00
|
|
|
// fn frobnicate(x: i32$0) {}
|
2020-08-19 11:44:33 -05:00
|
|
|
//
|
|
|
|
// 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| {
|
2020-12-05 07:28:15 -06:00
|
|
|
builder.delete(range_to_remove(param.syntax()));
|
2021-01-12 08:51:02 -06:00
|
|
|
for (file_id, references) in fn_def.usages(&ctx.sema).all() {
|
|
|
|
process_usages(ctx, builder, file_id, references, param_position);
|
2020-08-19 11:44:33 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-01-11 17:05:07 -06:00
|
|
|
fn process_usages(
|
2020-08-19 11:44:33 -05:00
|
|
|
ctx: &AssistContext,
|
|
|
|
builder: &mut AssistBuilder,
|
2021-01-12 08:51:02 -06:00
|
|
|
file_id: FileId,
|
|
|
|
references: Vec<FileReference>,
|
2020-08-19 11:44:33 -05:00
|
|
|
arg_to_remove: usize,
|
2021-01-11 17:05:07 -06:00
|
|
|
) {
|
2021-01-12 08:51:02 -06:00
|
|
|
let source_file = ctx.sema.parse(file_id);
|
|
|
|
builder.edit_file(file_id);
|
|
|
|
for usage in references {
|
2021-01-11 17:05:07 -06:00
|
|
|
if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
|
|
|
|
builder.delete(text_range);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_usage(
|
|
|
|
source_file: &SourceFile,
|
|
|
|
FileReference { range, .. }: FileReference,
|
|
|
|
arg_to_remove: usize,
|
|
|
|
) -> Option<TextRange> {
|
|
|
|
let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
|
2020-11-09 12:18:26 -06:00
|
|
|
let call_expr_range = call_expr.expr()?.syntax().text_range();
|
2021-01-11 17:05:07 -06:00
|
|
|
if !call_expr_range.contains_range(range) {
|
2020-08-19 11:44:33 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
|
2021-01-11 17:05:07 -06:00
|
|
|
Some(range_to_remove(arg.syntax()))
|
2020-08-19 11:44:33 -05:00
|
|
|
}
|
|
|
|
|
2020-12-05 07:28:15 -06:00
|
|
|
fn range_to_remove(node: &SyntaxNode) -> TextRange {
|
|
|
|
let up_to_comma = next_prev().find_map(|dir| {
|
2020-08-19 11:44:33 -05:00
|
|
|
node.siblings_with_tokens(dir)
|
|
|
|
.filter_map(|it| it.into_token())
|
|
|
|
.find(|it| it.kind() == T![,])
|
2020-12-05 07:28:15 -06:00
|
|
|
.map(|it| (dir, it))
|
2020-08-19 11:44:33 -05:00
|
|
|
});
|
2020-12-05 07:28:15 -06:00
|
|
|
if let Some((dir, token)) = up_to_comma {
|
|
|
|
if node.next_sibling().is_some() {
|
|
|
|
let up_to_space = token
|
|
|
|
.siblings_with_tokens(dir)
|
|
|
|
.skip(1)
|
|
|
|
.take_while(|it| it.kind() == WHITESPACE)
|
|
|
|
.last()
|
|
|
|
.and_then(|it| it.into_token());
|
|
|
|
return node
|
|
|
|
.text_range()
|
|
|
|
.cover(up_to_space.map_or(token.text_range(), |it| it.text_range()));
|
|
|
|
}
|
|
|
|
node.text_range().cover(token.text_range())
|
|
|
|
} else {
|
|
|
|
node.text_range()
|
|
|
|
}
|
2020-08-19 11:44:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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) }
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo(x: i32, $0y: i32) { x; }
|
2020-08-19 11:44:33 -05:00
|
|
|
fn b() { foo(9, 2,) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn a() { foo(9) }
|
|
|
|
fn foo(x: i32) { x; }
|
|
|
|
fn b() { foo(9, ) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-12-05 07:28:15 -06:00
|
|
|
#[test]
|
|
|
|
fn remove_unused_first_param() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo($0x: i32, y: i32) { y; }
|
2020-12-05 07:28:15 -06:00
|
|
|
fn a() { foo(1, 2) }
|
|
|
|
fn b() { foo(1, 2,) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn foo(y: i32) { y; }
|
|
|
|
fn a() { foo(2) }
|
|
|
|
fn b() { foo(2,) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_unused_single_param() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo($0x: i32) { 0; }
|
2020-12-05 07:28:15 -06:00
|
|
|
fn a() { foo(1) }
|
|
|
|
fn b() { foo(1, ) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn foo() { 0; }
|
|
|
|
fn a() { foo() }
|
|
|
|
fn b() { foo( ) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_unused_surrounded_by_parms() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo(x: i32, $0y: i32, z: i32) { x; }
|
2020-12-05 07:28:15 -06:00
|
|
|
fn a() { foo(1, 2, 3) }
|
|
|
|
fn b() { foo(1, 2, 3,) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn foo(x: i32, z: i32) { x; }
|
|
|
|
fn a() { foo(1, 3) }
|
|
|
|
fn b() { foo(1, 3,) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-11-09 12:18:26 -06:00
|
|
|
#[test]
|
|
|
|
fn remove_unused_qualified_call() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
mod bar { pub fn foo(x: i32, $0y: i32) { x; } }
|
2020-11-09 12:18:26 -06:00
|
|
|
fn b() { bar::foo(9, 2) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
mod bar { pub fn foo(x: i32) { x; } }
|
|
|
|
fn b() { bar::foo(9) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_unused_turbofished_func() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
pub fn foo<T>(x: T, $0y: i32) { x; }
|
2020-11-09 12:18:26 -06:00
|
|
|
fn b() { foo::<i32>(9, 2) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
pub fn foo<T>(x: T) { x; }
|
|
|
|
fn b() { foo::<i32>(9) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_unused_generic_unused_param_func() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
pub fn foo<T>(x: i32, $0y: T) { x; }
|
2020-11-09 12:18:26 -06:00
|
|
|
fn b() { foo::<i32>(9, 2) }
|
|
|
|
fn b2() { foo(9, 2) }
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
pub fn foo<T>(x: i32) { x; }
|
|
|
|
fn b() { foo::<i32>(9) }
|
|
|
|
fn b2() { foo(9) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-08-19 11:44:33 -05:00
|
|
|
#[test]
|
|
|
|
fn keep_used() {
|
|
|
|
mark::check!(keep_used);
|
|
|
|
check_assist_not_applicable(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo(x: i32, $0y: i32) { y; }
|
2020-08-19 11:44:33 -05:00
|
|
|
fn main() { foo(9, 2) }
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
2020-11-09 12:06:11 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn remove_across_files() {
|
|
|
|
check_assist(
|
|
|
|
remove_unused_param,
|
|
|
|
r#"
|
|
|
|
//- /main.rs
|
2021-01-06 14:15:48 -06:00
|
|
|
fn foo(x: i32, $0y: i32) { x; }
|
2020-11-09 12:06:11 -06:00
|
|
|
|
|
|
|
mod foo;
|
|
|
|
|
|
|
|
//- /foo.rs
|
|
|
|
use super::foo;
|
|
|
|
|
|
|
|
fn bar() {
|
|
|
|
let _ = foo(1, 2);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
//- /main.rs
|
|
|
|
fn foo(x: i32) { x; }
|
|
|
|
|
|
|
|
mod foo;
|
|
|
|
|
|
|
|
//- /foo.rs
|
|
|
|
use super::foo;
|
|
|
|
|
|
|
|
fn bar() {
|
|
|
|
let _ = foo(1);
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
}
|
2020-08-19 11:44:33 -05:00
|
|
|
}
|