use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
use syntax::{
algo::find_node_at_range,
- ast::{self, ArgListOwner},
+ ast::{self, HasArgList},
AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
};
_ => return None,
};
let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
- let param_position = func.param_list()?.params().position(|it| it == param)?;
+ let is_self_present =
+ param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some();
+ // check if fn is in impl Trait for ..
+ if func
+ .syntax()
+ .parent() // AssocItemList
+ .and_then(|x| x.parent())
+ .and_then(ast::Impl::cast)
+ .map_or(false, |imp| imp.trait_().is_some())
+ {
+ cov_mark::hit!(trait_impl);
+ return None;
+ }
+
+ let mut param_position = func.param_list()?.params().position(|it| it == param)?;
+ // param_list() does not take the self param into consideration, hence this additional check
+ // is required. For associated functions, param_position is incremented here. For inherent
+ // calls we revet the increment below, in process_usage, as those calls will not have an
+ // explicit self parameter.
+ if is_self_present {
+ param_position += 1;
+ }
let fn_def = {
let func = ctx.sema.to_def(&func)?;
- Definition::ModuleDef(func.into())
+ Definition::Function(func)
};
let param_def = {
|builder| {
builder.delete(range_to_remove(param.syntax()));
for (file_id, references) in fn_def.usages(&ctx.sema).all() {
- process_usages(ctx, builder, file_id, references, param_position);
+ process_usages(ctx, builder, file_id, references, param_position, is_self_present);
}
},
)
file_id: FileId,
references: Vec<FileReference>,
arg_to_remove: usize,
+ is_self_present: bool,
) {
let source_file = ctx.sema.parse(file_id);
builder.edit_file(file_id);
for usage in references {
- if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
+ if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove, is_self_present)
+ {
builder.delete(text_range);
}
}
fn process_usage(
source_file: &SourceFile,
FileReference { range, .. }: FileReference,
- arg_to_remove: usize,
+ mut arg_to_remove: usize,
+ is_self_present: bool,
) -> Option<TextRange> {
- let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
- let call_expr_range = call_expr.expr()?.syntax().text_range();
- if !call_expr_range.contains_range(range) {
- return None;
+ 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();
+ if !call_expr_range.contains_range(range) {
+ return None;
+ }
+
+ let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
+ return Some(range_to_remove(arg.syntax()));
}
- let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
- Some(range_to_remove(arg.syntax()))
+
+ let method_call_expr_opt: Option<ast::MethodCallExpr> =
+ find_node_at_range(source_file.syntax(), range);
+ if let Some(method_call_expr) = method_call_expr_opt {
+ let method_call_expr_range = method_call_expr.name_ref()?.syntax().text_range();
+ if !method_call_expr_range.contains_range(range) {
+ return None;
+ }
+
+ if is_self_present {
+ arg_to_remove -= 1;
+ }
+
+ let arg = method_call_expr.arg_list()?.args().nth(arg_to_remove)?;
+ return Some(range_to_remove(arg.syntax()));
+ }
+
+ None
}
-fn range_to_remove(node: &SyntaxNode) -> TextRange {
+pub(crate) fn range_to_remove(node: &SyntaxNode) -> TextRange {
let up_to_comma = next_prev().find_map(|dir| {
node.siblings_with_tokens(dir)
.filter_map(|it| it.into_token())
);
}
+ #[test]
+ fn trait_impl() {
+ cov_mark::check!(trait_impl);
+ check_assist_not_applicable(
+ remove_unused_param,
+ r#"
+trait Trait {
+ fn foo(x: i32);
+}
+impl Trait for () {
+ fn foo($0x: i32) {}
+}
+"#,
+ );
+ }
+
#[test]
fn remove_across_files() {
check_assist(
fn bar() {
let _ = foo(1);
}
+"#,
+ )
+ }
+
+ #[test]
+ fn test_remove_method_param() {
+ check_assist(
+ remove_unused_param,
+ r#"
+struct S;
+impl S { fn f(&self, $0_unused: i32) {} }
+fn main() {
+ S.f(92);
+ S.f();
+ S.f(93, 92);
+ S::f(&S, 92);
+}
+"#,
+ r#"
+struct S;
+impl S { fn f(&self) {} }
+fn main() {
+ S.f();
+ S.f();
+ S.f(92);
+ S::f(&S);
+}
"#,
)
}