Unnamed repository; edit this file 'description' to name the repository.
internal: Migrate `remove_unused_param` assist to `SyntaxEditor`
Giga Bowser 2025-02-25
parent 93c9f06 · commit f155aef
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_param.rs62
-rw-r--r--crates/syntax/src/algo.rs24
-rw-r--r--docs/book/src/assists_generated.md2
3 files changed, 71 insertions, 17 deletions
diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs
index 75120768da..5ddb17b207 100644
--- a/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -1,8 +1,9 @@
use ide_db::{defs::Definition, search::FileReference, EditionedFileId};
use syntax::{
- algo::find_node_at_range,
+ algo::{find_node_at_range, least_common_ancestor_element},
ast::{self, HasArgList},
- AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
+ syntax_editor::Element,
+ AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
};
use SyntaxKind::WHITESPACE;
@@ -74,15 +75,21 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
cov_mark::hit!(keep_used);
return None;
}
+ let parent = param.syntax().parent()?;
acc.add(
AssistId("remove_unused_param", AssistKind::Refactor),
"Remove unused parameter",
param.syntax().text_range(),
|builder| {
- builder.delete(range_to_remove(param.syntax()));
+ let mut editor = builder.make_editor(&parent);
+ let elements = elements_to_remove(param.syntax());
+ for element in elements {
+ editor.delete(element);
+ }
for (file_id, references) in fn_def.usages(&ctx.sema).all() {
process_usages(ctx, builder, file_id, references, param_position, is_self_present);
}
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
@@ -96,20 +103,24 @@ fn process_usages(
is_self_present: bool,
) {
let source_file = ctx.sema.parse(file_id);
- builder.edit_file(file_id);
let possible_ranges = references
.into_iter()
.filter_map(|usage| process_usage(&source_file, usage, arg_to_remove, is_self_present));
- let mut ranges_to_delete: Vec<TextRange> = vec![];
- for range in possible_ranges {
- if !ranges_to_delete.iter().any(|it| it.contains_range(range)) {
- ranges_to_delete.push(range)
+ for element_range in possible_ranges {
+ let Some(SyntaxElement::Node(parent)) = element_range
+ .iter()
+ .cloned()
+ .reduce(|a, b| least_common_ancestor_element(&a, &b).unwrap().syntax_element())
+ else {
+ continue;
+ };
+ let mut editor = builder.make_editor(&parent);
+ for element in element_range {
+ editor.delete(element);
}
- }
- for range in ranges_to_delete {
- builder.delete(range)
+ builder.add_file_edits(file_id, editor);
}
}
@@ -118,7 +129,7 @@ fn process_usage(
FileReference { range, .. }: FileReference,
mut arg_to_remove: usize,
is_self_present: bool,
-) -> Option<TextRange> {
+) -> Option<Vec<SyntaxElement>> {
let call_expr_opt: Option<ast::CallExpr> = find_node_at_range(source_file.syntax(), range);
if let Some(call_expr) = call_expr_opt {
let call_expr_range = call_expr.expr()?.syntax().text_range();
@@ -127,7 +138,7 @@ fn process_usage(
}
let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
- return Some(range_to_remove(arg.syntax()));
+ return Some(elements_to_remove(arg.syntax()));
}
let method_call_expr_opt: Option<ast::MethodCallExpr> =
@@ -143,7 +154,7 @@ fn process_usage(
}
let arg = method_call_expr.arg_list()?.args().nth(arg_to_remove)?;
- return Some(range_to_remove(arg.syntax()));
+ return Some(elements_to_remove(arg.syntax()));
}
None
@@ -174,6 +185,29 @@ pub(crate) fn range_to_remove(node: &SyntaxNode) -> TextRange {
}
}
+pub(crate) fn elements_to_remove(node: &SyntaxNode) -> Vec<SyntaxElement> {
+ let up_to_comma = next_prev().find_map(|dir| {
+ node.siblings_with_tokens(dir)
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == T![,])
+ .map(|it| (dir, it))
+ });
+ if let Some((dir, token)) = up_to_comma {
+ let after = token.siblings_with_tokens(dir).nth(1).unwrap();
+ let mut result: Vec<_> =
+ node.siblings_with_tokens(dir).take_while(|it| it != &after).collect();
+ if node.next_sibling().is_some() {
+ result.extend(
+ token.siblings_with_tokens(dir).skip(1).take_while(|it| it.kind() == WHITESPACE),
+ );
+ }
+
+ result
+ } else {
+ vec![node.syntax_element()]
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 2acb215831..3b85b137aa 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -3,8 +3,8 @@
use itertools::Itertools;
use crate::{
- AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
- TextSize,
+ syntax_editor::Element, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
+ SyntaxToken, TextRange, TextSize,
};
/// Returns ancestors of the node at the offset, sorted by length. This should
@@ -89,6 +89,26 @@ pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNod
Some(res)
}
+pub fn least_common_ancestor_element(u: impl Element, v: impl Element) -> Option<SyntaxNode> {
+ let u = u.syntax_element();
+ let v = v.syntax_element();
+ if u == v {
+ return match u {
+ NodeOrToken::Node(node) => Some(node),
+ NodeOrToken::Token(token) => token.parent(),
+ };
+ }
+
+ let u_depth = u.ancestors().count();
+ let v_depth = v.ancestors().count();
+ let keep = u_depth.min(v_depth);
+
+ let u_candidates = u.ancestors().skip(u_depth - keep);
+ let v_candidates = v.ancestors().skip(v_depth - keep);
+ let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?;
+ Some(res)
+}
+
pub fn neighbor<T: AstNode>(me: &T, direction: Direction) -> Option<T> {
me.syntax().siblings(direction).skip(1).find_map(T::cast)
}
diff --git a/docs/book/src/assists_generated.md b/docs/book/src/assists_generated.md
index a05536e2d2..2d233ca62a 100644
--- a/docs/book/src/assists_generated.md
+++ b/docs/book/src/assists_generated.md
@@ -3015,7 +3015,7 @@ mod foo {
### `remove_unused_param`
-**Source:** [remove_unused_param.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_unused_param.rs#L15)
+**Source:** [remove_unused_param.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/remove_unused_param.rs#L16)
Removes unused function parameter.