ide_assists/handlers/
bind_unused_param.rs1use crate::assist_context::{AssistContext, Assists};
2use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition};
3use syntax::{
4 AstNode,
5 ast::{self, HasName, edit_in_place::Indent},
6};
7
8pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
22 let param: ast::Param = ctx.find_node_at_offset()?;
23
24 let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None };
25 let name = ident_pat.name().filter(|n| !n.text().starts_with('_'))?;
26
27 let param_def = {
28 let local = ctx.sema.to_def(&ident_pat)?;
29 Definition::Local(local)
30 };
31 if param_def.usages(&ctx.sema).at_least_one() {
32 cov_mark::hit!(keep_used);
33 return None;
34 }
35
36 let func = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
37 let stmt_list = func.body()?.stmt_list()?;
38 let l_curly_range = stmt_list.l_curly_token()?.text_range();
39 let r_curly_range = stmt_list.r_curly_token()?.text_range();
40
41 acc.add(
42 AssistId::quick_fix("bind_unused_param"),
43 format!("Bind as `let _ = {name};`"),
44 param.syntax().text_range(),
45 |builder| {
46 let line_index = ctx.db().line_index(ctx.vfs_file_id());
47
48 let indent = func.indent_level();
49 let text_indent = indent + 1;
50 let mut text = format!("\n{text_indent}let _ = {name};");
51
52 let left_line = line_index.line_col(l_curly_range.end()).line;
53 let right_line = line_index.line_col(r_curly_range.start()).line;
54
55 if left_line == right_line {
56 cov_mark::hit!(single_line);
57 text.push_str(&format!("\n{indent}"));
58 }
59
60 builder.insert(l_curly_range.end(), text);
61 },
62 )
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::tests::{check_assist, check_assist_not_applicable};
68
69 use super::*;
70
71 #[test]
72 fn bind_unused_empty_block() {
73 cov_mark::check!(single_line);
74 check_assist(
75 bind_unused_param,
76 r#"
77fn foo($0y: i32) {}
78"#,
79 r#"
80fn foo(y: i32) {
81 let _ = y;
82}
83"#,
84 );
85 }
86
87 #[test]
88 fn bind_unused_ref_ident_pat() {
89 cov_mark::check!(single_line);
90 check_assist(
91 bind_unused_param,
92 r#"
93fn foo(ref $0y: i32) {}
94"#,
95 r#"
96fn foo(ref y: i32) {
97 let _ = y;
98}
99"#,
100 );
101 }
102
103 #[test]
104 fn bind_unused_empty_block_with_newline() {
105 check_assist(
106 bind_unused_param,
107 r#"
108fn foo($0y: i32) {
109}
110"#,
111 r#"
112fn foo(y: i32) {
113 let _ = y;
114}
115"#,
116 );
117 }
118
119 #[test]
120 fn bind_unused_generic() {
121 check_assist(
122 bind_unused_param,
123 r#"
124fn foo<T>($0y: T)
125where T : Default {
126}
127"#,
128 r#"
129fn foo<T>(y: T)
130where T : Default {
131 let _ = y;
132}
133"#,
134 );
135 }
136
137 #[test]
138 fn trait_impl() {
139 check_assist(
140 bind_unused_param,
141 r#"
142trait Trait {
143 fn foo(x: i32);
144}
145impl Trait for () {
146 fn foo($0x: i32) {}
147}
148"#,
149 r#"
150trait Trait {
151 fn foo(x: i32);
152}
153impl Trait for () {
154 fn foo(x: i32) {
155 let _ = x;
156 }
157}
158"#,
159 );
160 }
161
162 #[test]
163 fn keep_used() {
164 cov_mark::check!(keep_used);
165 check_assist_not_applicable(
166 bind_unused_param,
167 r#"
168fn foo(x: i32, $0y: i32) { y; }
169"#,
170 );
171 }
172
173 #[test]
174 fn keep_underscore_used() {
175 check_assist_not_applicable(
176 bind_unused_param,
177 r#"
178fn foo($0_x: i32, y: i32) {}
179"#,
180 );
181 }
182
183 #[test]
184 fn not_applicable_closure() {
185 check_assist_not_applicable(
186 bind_unused_param,
187 r#"
188fn foo() {
189 let _ = |$0x| 2;
190}
191"#,
192 );
193 }
194}