Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit e457759

Browse files
committed
Add bind_unused_param assistant.
1 parent 029baaa commit e457759

File tree

4 files changed

+156
-7
lines changed

4 files changed

+156
-7
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use crate::assist_context::{AssistContext, Assists};
2+
use ide_db::{
3+
assists::{AssistId, AssistKind},
4+
defs::Definition,
5+
LineIndexDatabase,
6+
};
7+
use syntax::{
8+
ast::{self, edit_in_place::Indent},
9+
AstNode,
10+
};
11+
12+
use super::remove_unused_param::is_trait_impl;
13+
14+
// Assist: bind_unused_param
15+
//
16+
// Binds unused function parameter to an underscore.
17+
//
18+
// ```
19+
// fn some_function(x: i32$0) {}
20+
// ```
21+
// ->
22+
// ```
23+
// fn some_function(x: i32) {
24+
// let _ = x;
25+
// }
26+
// ```
27+
pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
28+
let param: ast::Param = ctx.find_node_at_offset()?;
29+
30+
let ident_pat = match param.pat()? {
31+
ast::Pat::IdentPat(it) => it,
32+
_ => return None,
33+
};
34+
35+
let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
36+
if is_trait_impl(&func) {
37+
cov_mark::hit!(trait_impl);
38+
return None;
39+
}
40+
41+
let param_def = {
42+
let local = ctx.sema.to_def(&ident_pat)?;
43+
Definition::Local(local)
44+
};
45+
if param_def.usages(&ctx.sema).at_least_one() {
46+
cov_mark::hit!(keep_used);
47+
return None;
48+
}
49+
50+
let line_index = ctx.db().line_index(ctx.file_id());
51+
52+
let mut indent = func.indent_level();
53+
indent.0 += 1;
54+
let mut text = format!("\n{indent}let _ = {ident_pat};");
55+
56+
let stmt_list = func.body()?.stmt_list()?;
57+
let l_curly_range = stmt_list.l_curly_token()?.text_range();
58+
let r_curly_range = stmt_list.r_curly_token()?.text_range();
59+
let left_line = line_index.line_col(l_curly_range.end()).line;
60+
let right_line = line_index.line_col(r_curly_range.start()).line;
61+
62+
if left_line == right_line {
63+
text.push('\n');
64+
}
65+
66+
acc.add(
67+
AssistId("bind_unused_param", AssistKind::Refactor),
68+
&format!("Bind as `let _ = {};`", &ident_pat),
69+
param.syntax().text_range(),
70+
|builder| {
71+
builder.insert(l_curly_range.end(), text);
72+
},
73+
)
74+
}
75+
76+
#[cfg(test)]
77+
mod tests {
78+
use crate::tests::check_assist;
79+
80+
use super::*;
81+
82+
#[test]
83+
fn bind_unused_empty_block() {
84+
check_assist(
85+
bind_unused_param,
86+
r#"
87+
fn foo($0y: i32) {}
88+
"#,
89+
r#"
90+
fn foo(y: i32) {
91+
let _ = y;
92+
}
93+
"#,
94+
);
95+
}
96+
97+
#[test]
98+
fn bind_unused_empty_block_with_newline() {
99+
check_assist(
100+
bind_unused_param,
101+
r#"
102+
fn foo($0y: i32) {
103+
}
104+
"#,
105+
r#"
106+
fn foo(y: i32) {
107+
let _ = y;
108+
}
109+
"#,
110+
);
111+
}
112+
113+
#[test]
114+
fn bind_unused_generic() {
115+
check_assist(
116+
bind_unused_param,
117+
r#"
118+
fn foo<T>($0y: T)
119+
where T : Default {
120+
}
121+
"#,
122+
r#"
123+
fn foo<T>(y: T)
124+
where T : Default {
125+
let _ = y;
126+
}
127+
"#,
128+
);
129+
}
130+
}

crates/ide-assists/src/handlers/remove_unused_param.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
4242
param.syntax().parent()?.children().find_map(ast::SelfParam::cast).is_some();
4343

4444
// check if fn is in impl Trait for ..
45-
if func
46-
.syntax()
47-
.parent() // AssocItemList
48-
.and_then(|x| x.parent())
49-
.and_then(ast::Impl::cast)
50-
.map_or(false, |imp| imp.trait_().is_some())
51-
{
45+
if is_trait_impl(&func) {
5246
cov_mark::hit!(trait_impl);
5347
return None;
5448
}
@@ -87,6 +81,14 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
8781
)
8882
}
8983

84+
pub(crate) fn is_trait_impl(func: &ast::Fn) -> bool {
85+
func.syntax()
86+
.parent() // AssocItemList
87+
.and_then(|x| x.parent())
88+
.and_then(ast::Impl::cast)
89+
.map_or(false, |imp| imp.trait_().is_some())
90+
}
91+
9092
fn process_usages(
9193
ctx: &AssistContext<'_>,
9294
builder: &mut SourceChangeBuilder,

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ mod handlers {
114114
mod add_turbo_fish;
115115
mod apply_demorgan;
116116
mod auto_import;
117+
mod bind_unused_param;
117118
mod change_visibility;
118119
mod convert_bool_then;
119120
mod convert_comment_block;
@@ -224,6 +225,7 @@ mod handlers {
224225
add_turbo_fish::add_turbo_fish,
225226
apply_demorgan::apply_demorgan,
226227
auto_import::auto_import,
228+
bind_unused_param::bind_unused_param,
227229
change_visibility::change_visibility,
228230
convert_bool_then::convert_bool_then_to_if,
229231
convert_bool_then::convert_if_to_bool_then,

crates/ide-assists/src/tests/generated.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,21 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
265265
)
266266
}
267267

268+
#[test]
269+
fn doctest_bind_unused_param() {
270+
check_doc_test(
271+
"bind_unused_param",
272+
r#####"
273+
fn some_function(x: i32$0) {}
274+
"#####,
275+
r#####"
276+
fn some_function(x: i32) {
277+
let _ = x;
278+
}
279+
"#####,
280+
)
281+
}
282+
268283
#[test]
269284
fn doctest_change_visibility() {
270285
check_doc_test(

0 commit comments

Comments
 (0)