Delete related whitespace when removing unused param

Include adjacent whitespace in the text edits to remove the
parameter declaration and its occurences in calling code.

https://github.com/rust-analyzer/rust-analyzer/issues/6663
This commit is contained in:
Rüdiger Herrmann 2020-12-05 14:28:15 +01:00
parent c8a73fe655
commit 658ba9e29c

View file

@ -2,9 +2,10 @@ use ide_db::{defs::Definition, search::Reference};
use syntax::{ use syntax::{
algo::find_node_at_range, algo::find_node_at_range,
ast::{self, ArgListOwner}, ast::{self, ArgListOwner},
AstNode, SyntaxNode, TextRange, T, AstNode, SyntaxKind, SyntaxNode, TextRange, T,
}; };
use test_utils::mark; use test_utils::mark;
use SyntaxKind::WHITESPACE;
use crate::{ use crate::{
assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
@ -56,7 +57,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
"Remove unused parameter", "Remove unused parameter",
param.syntax().text_range(), param.syntax().text_range(),
|builder| { |builder| {
builder.delete(range_with_coma(param.syntax())); builder.delete(range_to_remove(param.syntax()));
for usage in fn_def.usages(&ctx.sema).all() { for usage in fn_def.usages(&ctx.sema).all() {
process_usage(ctx, builder, usage, param_position); process_usage(ctx, builder, usage, param_position);
} }
@ -80,19 +81,34 @@ fn process_usage(
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
builder.edit_file(usage.file_range.file_id); builder.edit_file(usage.file_range.file_id);
builder.delete(range_with_coma(arg.syntax())); builder.delete(range_to_remove(arg.syntax()));
Some(()) Some(())
} }
fn range_with_coma(node: &SyntaxNode) -> TextRange { fn range_to_remove(node: &SyntaxNode) -> TextRange {
let up_to = next_prev().find_map(|dir| { let up_to_comma = next_prev().find_map(|dir| {
node.siblings_with_tokens(dir) node.siblings_with_tokens(dir)
.filter_map(|it| it.into_token()) .filter_map(|it| it.into_token())
.find(|it| it.kind() == T![,]) .find(|it| it.kind() == T![,])
.map(|it| (dir, it))
}); });
let up_to = up_to.map_or(node.text_range(), |it| it.text_range()); if let Some((dir, token)) = up_to_comma {
node.text_range().cover(up_to) 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()
}
} }
#[cfg(test)] #[cfg(test)]
@ -118,6 +134,57 @@ fn b() { foo(9, ) }
); );
} }
#[test]
fn remove_unused_first_param() {
check_assist(
remove_unused_param,
r#"
fn foo(<|>x: i32, y: i32) { y; }
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#"
fn foo(<|>x: i32) { 0; }
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#"
fn foo(x: i32, <|>y: i32, z: i32) { x; }
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,) }
"#,
);
}
#[test] #[test]
fn remove_unused_qualified_call() { fn remove_unused_qualified_call() {
check_assist( check_assist(