ide_diagnostics/handlers/
explicit_drop_method_use.rs1use either::Either;
2use hir::InFile;
3use ide_db::assists::Assist;
4use ide_db::source_change::{SourceChange, SourceChangeBuilder};
5use ide_db::text_edit::TextEdit;
6use itertools::Itertools;
7use syntax::{
8 AstNode, AstPtr,
9 ast::{self, HasArgList},
10};
11
12use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix};
13
14pub(crate) fn explicit_drop_method_use(
18 ctx: &DiagnosticsContext<'_, '_>,
19 d: &hir::ExplicitDropMethodUse,
20) -> Diagnostic {
21 match d.expr_or_path {
22 Either::Left(expr) => {
23 let display_range = adjusted_display_range(ctx, expr, &|node| {
24 Some(node.name_ref()?.syntax().text_range())
25 });
26 Diagnostic::new(
27 DiagnosticCode::RustcHardError("E0040"),
28 "explicit use of destructor method",
29 display_range,
30 )
31 .stable()
32 .with_main_node(expr.map(Into::into))
33 .with_fixes(fix_method_call(ctx, expr))
34 }
35 Either::Right(path) => Diagnostic::new_with_syntax_node_ptr(
36 ctx,
37 DiagnosticCode::RustcHardError("E0040"),
38 "explicit use of destructor method",
39 path.map(Into::into),
40 )
41 .stable()
42 .with_fixes(fix_path(ctx, path)),
43 }
44}
45
46fn fix_method_call(
47 ctx: &DiagnosticsContext<'_, '_>,
48 mcall_ptr: InFile<AstPtr<ast::MethodCallExpr>>,
49) -> Option<Vec<Assist>> {
50 if mcall_ptr.file_id.is_macro() {
51 return None;
55 }
56
57 let db = ctx.db();
58
59 let file_id = mcall_ptr.file_id;
60 let mcall = mcall_ptr.to_node(db);
61 let range = mcall.syntax().text_range();
62
63 let recv = mcall.receiver()?;
68
69 let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db));
70 let editor = builder.make_editor(mcall.syntax());
71 let make = editor.make();
72 let new_call =
73 make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv]));
74 builder.replace_ast(ast::Expr::MethodCallExpr(mcall), ast::Expr::CallExpr(new_call));
75 let source_change = builder.finish();
76 Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)])
77}
78
79fn fix_path(
80 ctx: &DiagnosticsContext<'_, '_>,
81 path_ptr: InFile<AstPtr<ast::Path>>,
82) -> Option<Vec<Assist>> {
83 let db = ctx.db();
84
85 let file_id = path_ptr.file_id;
86 let path = path_ptr.to_node(db);
87
88 if let Some(call) =
89 path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast)
90 {
91 if file_id.is_macro() {
92 return None;
95 }
96
97 let arg_list = call.arg_list()?;
99 let ref_recv = arg_list.args().exactly_one().ok()?;
100 let ast::Expr::RefExpr(ref_recv) = ref_recv else {
101 return None;
102 };
103 let recv = ref_recv.expr()?;
104
105 let range = call.syntax().text_range();
106
107 let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db));
108 let editor = builder.make_editor(call.syntax());
109 let make = editor.make();
110 let new_call =
111 make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv]));
112 builder.replace_ast(call, new_call);
113 let source_change = builder.finish();
114 Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)])
115 } else {
116 let range = InFile::new(file_id, path.syntax().text_range())
120 .original_node_file_range_rooted_opt(db)?;
121
122 let edit = TextEdit::replace(range.range, "drop".to_owned());
123 let source_change = SourceChange::from_text_edit(range.file_id.file_id(db), edit);
124 Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range.range)])
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use crate::tests::{
131 check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled,
132 };
133
134 #[test]
135 fn method_call_diagnostic() {
136 check_diagnostics(
137 r#"
138//- minicore: drop
139struct A;
140impl Drop for A { fn drop(&mut self) {} }
141
142fn main(mut a: A) {
143 a.drop();
144 // ^^^^ 💡 error: explicit use of destructor method
145}
146"#,
147 );
148 }
149
150 #[test]
151 fn method_call_fix() {
152 check_fix(
153 r#"
154//- minicore: drop
155struct A;
156impl Drop for A { fn drop(&mut self) {} }
157
158fn main(mut a: A) {
159 a.drop$0();
160}
161"#,
162 r#"
163struct A;
164impl Drop for A { fn drop(&mut self) {} }
165
166fn main(mut a: A) {
167 drop(a);
168}
169"#,
170 );
171 }
172
173 #[test]
174 fn qualified_call_1_diagnostic() {
175 check_diagnostics(
176 r#"
177//- minicore: drop
178struct A;
179impl Drop for A { fn drop(&mut self) {} }
180
181fn main(mut a: A) {
182 A::drop(&mut a);
183 // ^^^^^^^ 💡 error: explicit use of destructor method
184}
185"#,
186 );
187 }
188
189 #[test]
190 fn qualified_call_1_fix() {
191 check_fix(
192 r#"
193//- minicore: drop
194struct A;
195impl Drop for A { fn drop(&mut self) {} }
196
197fn main(mut a: A) {
198 A::drop(&mut a$0);
199}
200"#,
201 r#"
202struct A;
203impl Drop for A { fn drop(&mut self) {} }
204
205fn main(mut a: A) {
206 drop(a);
207}
208"#,
209 )
210 }
211
212 #[test]
213 fn qualified_call_2_diagnostic() {
214 check_diagnostics(
215 r#"
216//- minicore: drop
217struct A;
218impl Drop for A { fn drop(&mut self) {} }
219
220fn main(mut a: A) {
221 Drop::drop(&mut a);
222 // ^^^^^^^^^^ 💡 error: explicit use of destructor method
223}
224"#,
225 );
226 }
227
228 #[test]
229 fn qualified_call_2_fix() {
230 check_fix(
231 r#"
232//- minicore: drop
233struct A;
234impl Drop for A { fn drop(&mut self) {} }
235
236fn main(mut a: A) {
237 Drop::drop(&mut a$0);
238}
239"#,
240 r#"
241struct A;
242impl Drop for A { fn drop(&mut self) {} }
243
244fn main(mut a: A) {
245 drop(a);
246}
247"#,
248 )
249 }
250
251 #[test]
252 fn fully_qualified_call_diagnostic() {
253 check_diagnostics(
254 r#"
255//- minicore: drop
256struct A;
257impl Drop for A { fn drop(&mut self) {} }
258
259fn main(mut a: A) {
260 <A as Drop>::drop(&mut a);
261 // ^^^^^^^^^^^^^^^^^ 💡 error: explicit use of destructor method
262}
263"#,
264 );
265 }
266
267 #[test]
268 fn fully_qualified_call_fix() {
269 check_fix(
270 r#"
271//- minicore: drop
272struct A;
273impl Drop for A { fn drop(&mut self) {} }
274
275fn main(mut a: A) {
276 <A as Drop>::drop(&mut a$0);
277}
278"#,
279 r#"
280struct A;
281impl Drop for A { fn drop(&mut self) {} }
282
283fn main(mut a: A) {
284 drop(a);
285}
286"#,
287 )
288 }
289
290 #[test]
291 fn path_diagnostic() {
292 check_diagnostics_with_disabled(
293 r#"
294//- minicore: drop
295struct A;
296impl Drop for A { fn drop(&mut self) {} }
297
298fn main(mut a: A) {
299 let d = A::drop;
300 // ^^^^^^^ 💡 error: explicit use of destructor method
301 d(&mut a);
302}
303"#,
304 &["unused_variables"],
308 );
309 }
310
311 #[test]
312 fn path_fix() {
315 check_fix_with_disabled(
316 r#"
317//- minicore: drop
318struct A;
319impl Drop for A { fn drop(&mut self) {} }
320
321fn main(mut a: A) {
322 let d = A::drop$0;
323 d(&mut a);
324}
325"#,
326 r#"
327struct A;
328impl Drop for A { fn drop(&mut self) {} }
329
330fn main(mut a: A) {
331 let d = drop;
332 d(&mut a);
333}
334"#,
335 &["unused_variables"],
339 );
340 }
341
342 #[test]
343 fn path_fix_in_macro() {
346 check_fix(
347 r#"
348//- minicore: drop
349struct A;
350impl Drop for A { fn drop(&mut self) {} }
351
352macro_rules! main {
353 ($e:expr) => {
354 fn main() { $e }
355 }
356}
357
358main!{{
359 let mut a = A;
360 let d = A::drop$0;
361 d(&mut a);
362}};
363"#,
364 r#"
365struct A;
366impl Drop for A { fn drop(&mut self) {} }
367
368macro_rules! main {
369 ($e:expr) => {
370 fn main() { $e }
371 }
372}
373
374main!{{
375 let mut a = A;
376 let d = drop;
377 d(&mut a);
378}};
379"#,
380 );
381 }
382
383 #[test]
384 fn std_mem_drop() {
385 check_diagnostics(
386 r#"
387//- minicore: drop
388struct A;
389impl Drop for A { fn drop(&mut self) {} }
390
391fn main(a: A) {
392 drop(a);
393}
394"#,
395 );
396 }
397
398 #[test]
399 fn inherent_drop_method() {
400 check_diagnostics(
401 r#"
402struct A;
403impl A { fn drop(&mut self) {} }
404
405fn main(mut a: A) {
406 a.drop();
407}
408"#,
409 );
410 }
411
412 #[test]
413 fn custom_trait_drop_method() {
414 check_diagnostics(
415 r#"
416struct A;
417trait MyDrop { fn drop(&mut self); }
418impl MyDrop for A { fn drop(&mut self) {} }
419
420fn main(mut a: A) {
421 a.drop();
422}
423"#,
424 );
425 }
426}