about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGiga Bowser <45986823+Giga-Bowser@users.noreply.github.com>2024-12-04 19:55:16 -0500
committerGiga Bowser <45986823+Giga-Bowser@users.noreply.github.com>2025-02-25 11:52:13 -0500
commit6e6e7e966910e7dc5776f760f4848af740a1c581 (patch)
tree0cdc07fa979b746aa944e01ca875b9c69dd7ddd2
parent1d5f831ffc0010e3483b780808960da3a13b64e5 (diff)
downloadrust-6e6e7e966910e7dc5776f760f4848af740a1c581.tar.gz
rust-6e6e7e966910e7dc5776f760f4848af740a1c581.zip
internal: Migrate `remove_unused_param` assist to `SyntaxEditor`
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs62
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/algo.rs24
-rw-r--r--src/tools/rust-analyzer/docs/book/src/assists_generated.md2
3 files changed, 71 insertions, 17 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
index 75120768da0..5ddb17b2072 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
index 2acb2158318..3b85b137aa9 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs
+++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/docs/book/src/assists_generated.md b/src/tools/rust-analyzer/docs/book/src/assists_generated.md
index a05536e2d2d..2d233ca62ad 100644
--- a/src/tools/rust-analyzer/docs/book/src/assists_generated.md
+++ b/src/tools/rust-analyzer/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.