1 use hir::{InFile, ModuleDef};
2 use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
3 use itertools::Itertools;
4 use syntax::{
5 ast::{self, AstNode, HasName},
6 SyntaxKind::WHITESPACE,
7 };
8
9 use crate::{
10 assist_context::{AssistContext, Assists, SourceChangeBuilder},
11 utils::{
12 add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
13 generate_trait_impl_text, render_snippet, Cursor, DefaultMethods,
14 },
15 AssistId, AssistKind,
16 };
17
18 // Assist: replace_derive_with_manual_impl
19 //
20 // Converts a `derive` impl into a manual one.
21 //
22 // ```
23 // # //- minicore: derive
24 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
25 // #[derive(Deb$0ug, Display)]
26 // struct S;
27 // ```
28 // ->
29 // ```
30 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
31 // #[derive(Display)]
32 // struct S;
33 //
34 // impl Debug for S {
35 // $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
36 // f.debug_struct("S").finish()
37 // }
38 // }
39 // ```
replace_derive_with_manual_impl( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()>40 pub(crate) fn replace_derive_with_manual_impl(
41 acc: &mut Assists,
42 ctx: &AssistContext<'_>,
43 ) -> Option<()> {
44 let attr = ctx.find_node_at_offset_with_descend::<ast::Attr>()?;
45 let path = attr.path()?;
46 let hir_file = ctx.sema.hir_file_for(attr.syntax());
47 if !hir_file.is_derive_attr_pseudo_expansion(ctx.db()) {
48 return None;
49 }
50
51 let InFile { file_id, value } = hir_file.call_node(ctx.db())?;
52 if file_id.is_macro() {
53 // FIXME: make this work in macro files
54 return None;
55 }
56 // collect the derive paths from the #[derive] expansion
57 let current_derives = ctx
58 .sema
59 .parse_or_expand(hir_file)
60 .descendants()
61 .filter_map(ast::Attr::cast)
62 .filter_map(|attr| attr.path())
63 .collect::<Vec<_>>();
64
65 let adt = value.parent().and_then(ast::Adt::cast)?;
66 let attr = ast::Attr::cast(value)?;
67 let args = attr.token_tree()?;
68
69 let current_module = ctx.sema.scope(adt.syntax())?.module();
70 let current_crate = current_module.krate();
71
72 let found_traits = items_locator::items_with_name(
73 &ctx.sema,
74 current_crate,
75 NameToImport::exact_case_sensitive(path.segments().last()?.to_string()),
76 items_locator::AssocItemSearch::Exclude,
77 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
78 )
79 .filter_map(|item| match item.as_module_def()? {
80 ModuleDef::Trait(trait_) => Some(trait_),
81 _ => None,
82 })
83 .flat_map(|trait_| {
84 current_module
85 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std)
86 .as_ref()
87 .map(mod_path_to_ast)
88 .zip(Some(trait_))
89 });
90
91 let mut no_traits_found = true;
92 for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
93 add_assist(
94 acc,
95 ctx,
96 &attr,
97 ¤t_derives,
98 &args,
99 &path,
100 &replace_trait_path,
101 Some(trait_),
102 &adt,
103 )?;
104 }
105 if no_traits_found {
106 add_assist(acc, ctx, &attr, ¤t_derives, &args, &path, &path, None, &adt)?;
107 }
108 Some(())
109 }
110
add_assist( acc: &mut Assists, ctx: &AssistContext<'_>, attr: &ast::Attr, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, replace_trait_path: &ast::Path, trait_: Option<hir::Trait>, adt: &ast::Adt, ) -> Option<()>111 fn add_assist(
112 acc: &mut Assists,
113 ctx: &AssistContext<'_>,
114 attr: &ast::Attr,
115 old_derives: &[ast::Path],
116 old_tree: &ast::TokenTree,
117 old_trait_path: &ast::Path,
118 replace_trait_path: &ast::Path,
119 trait_: Option<hir::Trait>,
120 adt: &ast::Adt,
121 ) -> Option<()> {
122 let target = attr.syntax().text_range();
123 let annotated_name = adt.name()?;
124 let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
125
126 acc.add(
127 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
128 label,
129 target,
130 |builder| {
131 let insert_pos = adt.syntax().text_range().end();
132 let impl_def_with_items =
133 impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
134 update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
135 let trait_path = replace_trait_path.to_string();
136 match (ctx.config.snippet_cap, impl_def_with_items) {
137 (None, _) => {
138 builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
139 }
140 (Some(cap), None) => builder.insert_snippet(
141 cap,
142 insert_pos,
143 generate_trait_impl_text(adt, &trait_path, " $0"),
144 ),
145 (Some(cap), Some((impl_def, first_assoc_item))) => {
146 let mut cursor = Cursor::Before(first_assoc_item.syntax());
147 let placeholder;
148 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
149 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
150 {
151 if m.syntax().text() == "todo!()" {
152 placeholder = m;
153 cursor = Cursor::Replace(placeholder.syntax());
154 }
155 }
156 }
157
158 let rendered = render_snippet(cap, impl_def.syntax(), cursor);
159 builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}"))
160 }
161 };
162 },
163 )
164 }
165
impl_def_from_trait( sema: &hir::Semantics<'_, ide_db::RootDatabase>, adt: &ast::Adt, annotated_name: &ast::Name, trait_: Option<hir::Trait>, trait_path: &ast::Path, ) -> Option<(ast::Impl, ast::AssocItem)>166 fn impl_def_from_trait(
167 sema: &hir::Semantics<'_, ide_db::RootDatabase>,
168 adt: &ast::Adt,
169 annotated_name: &ast::Name,
170 trait_: Option<hir::Trait>,
171 trait_path: &ast::Path,
172 ) -> Option<(ast::Impl, ast::AssocItem)> {
173 let trait_ = trait_?;
174 let target_scope = sema.scope(annotated_name.syntax())?;
175 let trait_items = filter_assoc_items(sema, &trait_.items(sema.db), DefaultMethods::No);
176 if trait_items.is_empty() {
177 return None;
178 }
179 let impl_def = {
180 use syntax::ast::Impl;
181 let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
182 // FIXME: `generate_trait_impl_text` currently generates two newlines
183 // at the front, but these leading newlines should really instead be
184 // inserted at the same time the impl is inserted
185 assert_eq!(&text[..2], "\n\n", "`generate_trait_impl_text` output changed");
186 let parse = syntax::SourceFile::parse(&text[2..]);
187 let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
188 Some(it) => it,
189 None => {
190 panic!(
191 "Failed to make ast node `{}` from text {}",
192 std::any::type_name::<Impl>(),
193 text
194 )
195 }
196 };
197 let node = node.clone_for_update();
198 assert_eq!(node.syntax().text_range().start(), 0.into());
199 node
200 };
201
202 let first_assoc_item =
203 add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
204
205 // Generate a default `impl` function body for the derived trait.
206 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
207 let _ = gen_trait_fn_body(func, trait_path, adt, None);
208 };
209
210 Some((impl_def, first_assoc_item))
211 }
212
update_attribute( builder: &mut SourceChangeBuilder, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, attr: &ast::Attr, )213 fn update_attribute(
214 builder: &mut SourceChangeBuilder,
215 old_derives: &[ast::Path],
216 old_tree: &ast::TokenTree,
217 old_trait_path: &ast::Path,
218 attr: &ast::Attr,
219 ) {
220 let new_derives = old_derives
221 .iter()
222 .filter(|t| t.to_string() != old_trait_path.to_string())
223 .collect::<Vec<_>>();
224 let has_more_derives = !new_derives.is_empty();
225
226 if has_more_derives {
227 let new_derives = format!("({})", new_derives.iter().format(", "));
228 builder.replace(old_tree.syntax().text_range(), new_derives);
229 } else {
230 let attr_range = attr.syntax().text_range();
231 builder.delete(attr_range);
232
233 if let Some(line_break_range) = attr
234 .syntax()
235 .next_sibling_or_token()
236 .filter(|t| t.kind() == WHITESPACE)
237 .map(|t| t.text_range())
238 {
239 builder.delete(line_break_range);
240 }
241 }
242 }
243
244 #[cfg(test)]
245 mod tests {
246 use crate::tests::{check_assist, check_assist_not_applicable};
247
248 use super::*;
249
250 #[test]
add_custom_impl_debug_record_struct()251 fn add_custom_impl_debug_record_struct() {
252 check_assist(
253 replace_derive_with_manual_impl,
254 r#"
255 //- minicore: fmt, derive
256 #[derive(Debu$0g)]
257 struct Foo {
258 bar: String,
259 }
260 "#,
261 r#"
262 struct Foo {
263 bar: String,
264 }
265
266 impl core::fmt::Debug for Foo {
267 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
268 f.debug_struct("Foo").field("bar", &self.bar).finish()
269 }
270 }
271 "#,
272 )
273 }
274 #[test]
add_custom_impl_debug_tuple_struct()275 fn add_custom_impl_debug_tuple_struct() {
276 check_assist(
277 replace_derive_with_manual_impl,
278 r#"
279 //- minicore: fmt, derive
280 #[derive(Debu$0g)]
281 struct Foo(String, usize);
282 "#,
283 r#"struct Foo(String, usize);
284
285 impl core::fmt::Debug for Foo {
286 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
287 f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
288 }
289 }
290 "#,
291 )
292 }
293 #[test]
add_custom_impl_debug_empty_struct()294 fn add_custom_impl_debug_empty_struct() {
295 check_assist(
296 replace_derive_with_manual_impl,
297 r#"
298 //- minicore: fmt, derive
299 #[derive(Debu$0g)]
300 struct Foo;
301 "#,
302 r#"
303 struct Foo;
304
305 impl core::fmt::Debug for Foo {
306 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307 f.debug_struct("Foo").finish()
308 }
309 }
310 "#,
311 )
312 }
313 #[test]
add_custom_impl_debug_enum()314 fn add_custom_impl_debug_enum() {
315 check_assist(
316 replace_derive_with_manual_impl,
317 r#"
318 //- minicore: fmt, derive
319 #[derive(Debu$0g)]
320 enum Foo {
321 Bar,
322 Baz,
323 }
324 "#,
325 r#"
326 enum Foo {
327 Bar,
328 Baz,
329 }
330
331 impl core::fmt::Debug for Foo {
332 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
333 match self {
334 Self::Bar => write!(f, "Bar"),
335 Self::Baz => write!(f, "Baz"),
336 }
337 }
338 }
339 "#,
340 )
341 }
342
343 #[test]
add_custom_impl_debug_tuple_enum()344 fn add_custom_impl_debug_tuple_enum() {
345 check_assist(
346 replace_derive_with_manual_impl,
347 r#"
348 //- minicore: fmt, derive
349 #[derive(Debu$0g)]
350 enum Foo {
351 Bar(usize, usize),
352 Baz,
353 }
354 "#,
355 r#"
356 enum Foo {
357 Bar(usize, usize),
358 Baz,
359 }
360
361 impl core::fmt::Debug for Foo {
362 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
363 match self {
364 Self::Bar(arg0, arg1) => f.debug_tuple("Bar").field(arg0).field(arg1).finish(),
365 Self::Baz => write!(f, "Baz"),
366 }
367 }
368 }
369 "#,
370 )
371 }
372 #[test]
add_custom_impl_debug_record_enum()373 fn add_custom_impl_debug_record_enum() {
374 check_assist(
375 replace_derive_with_manual_impl,
376 r#"
377 //- minicore: fmt, derive
378 #[derive(Debu$0g)]
379 enum Foo {
380 Bar {
381 baz: usize,
382 qux: usize,
383 },
384 Baz,
385 }
386 "#,
387 r#"
388 enum Foo {
389 Bar {
390 baz: usize,
391 qux: usize,
392 },
393 Baz,
394 }
395
396 impl core::fmt::Debug for Foo {
397 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
398 match self {
399 Self::Bar { baz, qux } => f.debug_struct("Bar").field("baz", baz).field("qux", qux).finish(),
400 Self::Baz => write!(f, "Baz"),
401 }
402 }
403 }
404 "#,
405 )
406 }
407 #[test]
add_custom_impl_default_record_struct()408 fn add_custom_impl_default_record_struct() {
409 check_assist(
410 replace_derive_with_manual_impl,
411 r#"
412 //- minicore: default, derive
413 #[derive(Defau$0lt)]
414 struct Foo {
415 foo: usize,
416 }
417 "#,
418 r#"
419 struct Foo {
420 foo: usize,
421 }
422
423 impl Default for Foo {
424 $0fn default() -> Self {
425 Self { foo: Default::default() }
426 }
427 }
428 "#,
429 )
430 }
431 #[test]
add_custom_impl_default_tuple_struct()432 fn add_custom_impl_default_tuple_struct() {
433 check_assist(
434 replace_derive_with_manual_impl,
435 r#"
436 //- minicore: default, derive
437 #[derive(Defau$0lt)]
438 struct Foo(usize);
439 "#,
440 r#"
441 struct Foo(usize);
442
443 impl Default for Foo {
444 $0fn default() -> Self {
445 Self(Default::default())
446 }
447 }
448 "#,
449 )
450 }
451 #[test]
add_custom_impl_default_empty_struct()452 fn add_custom_impl_default_empty_struct() {
453 check_assist(
454 replace_derive_with_manual_impl,
455 r#"
456 //- minicore: default, derive
457 #[derive(Defau$0lt)]
458 struct Foo;
459 "#,
460 r#"
461 struct Foo;
462
463 impl Default for Foo {
464 $0fn default() -> Self {
465 Self { }
466 }
467 }
468 "#,
469 )
470 }
471
472 #[test]
add_custom_impl_hash_record_struct()473 fn add_custom_impl_hash_record_struct() {
474 check_assist(
475 replace_derive_with_manual_impl,
476 r#"
477 //- minicore: hash, derive
478 #[derive(Has$0h)]
479 struct Foo {
480 bin: usize,
481 bar: usize,
482 }
483 "#,
484 r#"
485 struct Foo {
486 bin: usize,
487 bar: usize,
488 }
489
490 impl core::hash::Hash for Foo {
491 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
492 self.bin.hash(state);
493 self.bar.hash(state);
494 }
495 }
496 "#,
497 )
498 }
499
500 #[test]
add_custom_impl_hash_tuple_struct()501 fn add_custom_impl_hash_tuple_struct() {
502 check_assist(
503 replace_derive_with_manual_impl,
504 r#"
505 //- minicore: hash, derive
506 #[derive(Has$0h)]
507 struct Foo(usize, usize);
508 "#,
509 r#"
510 struct Foo(usize, usize);
511
512 impl core::hash::Hash for Foo {
513 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
514 self.0.hash(state);
515 self.1.hash(state);
516 }
517 }
518 "#,
519 )
520 }
521
522 #[test]
add_custom_impl_hash_enum()523 fn add_custom_impl_hash_enum() {
524 check_assist(
525 replace_derive_with_manual_impl,
526 r#"
527 //- minicore: hash, derive
528 #[derive(Has$0h)]
529 enum Foo {
530 Bar,
531 Baz,
532 }
533 "#,
534 r#"
535 enum Foo {
536 Bar,
537 Baz,
538 }
539
540 impl core::hash::Hash for Foo {
541 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
542 core::mem::discriminant(self).hash(state);
543 }
544 }
545 "#,
546 )
547 }
548
549 #[test]
add_custom_impl_clone_record_struct()550 fn add_custom_impl_clone_record_struct() {
551 check_assist(
552 replace_derive_with_manual_impl,
553 r#"
554 //- minicore: clone, derive
555 #[derive(Clo$0ne)]
556 struct Foo {
557 bin: usize,
558 bar: usize,
559 }
560 "#,
561 r#"
562 struct Foo {
563 bin: usize,
564 bar: usize,
565 }
566
567 impl Clone for Foo {
568 $0fn clone(&self) -> Self {
569 Self { bin: self.bin.clone(), bar: self.bar.clone() }
570 }
571 }
572 "#,
573 )
574 }
575
576 #[test]
add_custom_impl_clone_tuple_struct()577 fn add_custom_impl_clone_tuple_struct() {
578 check_assist(
579 replace_derive_with_manual_impl,
580 r#"
581 //- minicore: clone, derive
582 #[derive(Clo$0ne)]
583 struct Foo(usize, usize);
584 "#,
585 r#"
586 struct Foo(usize, usize);
587
588 impl Clone for Foo {
589 $0fn clone(&self) -> Self {
590 Self(self.0.clone(), self.1.clone())
591 }
592 }
593 "#,
594 )
595 }
596
597 #[test]
add_custom_impl_clone_empty_struct()598 fn add_custom_impl_clone_empty_struct() {
599 check_assist(
600 replace_derive_with_manual_impl,
601 r#"
602 //- minicore: clone, derive
603 #[derive(Clo$0ne)]
604 struct Foo;
605 "#,
606 r#"
607 struct Foo;
608
609 impl Clone for Foo {
610 $0fn clone(&self) -> Self {
611 Self { }
612 }
613 }
614 "#,
615 )
616 }
617
618 #[test]
add_custom_impl_clone_enum()619 fn add_custom_impl_clone_enum() {
620 check_assist(
621 replace_derive_with_manual_impl,
622 r#"
623 //- minicore: clone, derive
624 #[derive(Clo$0ne)]
625 enum Foo {
626 Bar,
627 Baz,
628 }
629 "#,
630 r#"
631 enum Foo {
632 Bar,
633 Baz,
634 }
635
636 impl Clone for Foo {
637 $0fn clone(&self) -> Self {
638 match self {
639 Self::Bar => Self::Bar,
640 Self::Baz => Self::Baz,
641 }
642 }
643 }
644 "#,
645 )
646 }
647
648 #[test]
add_custom_impl_clone_tuple_enum()649 fn add_custom_impl_clone_tuple_enum() {
650 check_assist(
651 replace_derive_with_manual_impl,
652 r#"
653 //- minicore: clone, derive
654 #[derive(Clo$0ne)]
655 enum Foo {
656 Bar(String),
657 Baz,
658 }
659 "#,
660 r#"
661 enum Foo {
662 Bar(String),
663 Baz,
664 }
665
666 impl Clone for Foo {
667 $0fn clone(&self) -> Self {
668 match self {
669 Self::Bar(arg0) => Self::Bar(arg0.clone()),
670 Self::Baz => Self::Baz,
671 }
672 }
673 }
674 "#,
675 )
676 }
677
678 #[test]
add_custom_impl_clone_record_enum()679 fn add_custom_impl_clone_record_enum() {
680 check_assist(
681 replace_derive_with_manual_impl,
682 r#"
683 //- minicore: clone, derive
684 #[derive(Clo$0ne)]
685 enum Foo {
686 Bar {
687 bin: String,
688 },
689 Baz,
690 }
691 "#,
692 r#"
693 enum Foo {
694 Bar {
695 bin: String,
696 },
697 Baz,
698 }
699
700 impl Clone for Foo {
701 $0fn clone(&self) -> Self {
702 match self {
703 Self::Bar { bin } => Self::Bar { bin: bin.clone() },
704 Self::Baz => Self::Baz,
705 }
706 }
707 }
708 "#,
709 )
710 }
711
712 #[test]
add_custom_impl_partial_ord_record_struct()713 fn add_custom_impl_partial_ord_record_struct() {
714 check_assist(
715 replace_derive_with_manual_impl,
716 r#"
717 //- minicore: ord, derive
718 #[derive(Partial$0Ord)]
719 struct Foo {
720 bin: usize,
721 }
722 "#,
723 r#"
724 struct Foo {
725 bin: usize,
726 }
727
728 impl PartialOrd for Foo {
729 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
730 self.bin.partial_cmp(&other.bin)
731 }
732 }
733 "#,
734 )
735 }
736
737 #[test]
add_custom_impl_partial_ord_record_struct_multi_field()738 fn add_custom_impl_partial_ord_record_struct_multi_field() {
739 check_assist(
740 replace_derive_with_manual_impl,
741 r#"
742 //- minicore: ord, derive
743 #[derive(Partial$0Ord)]
744 struct Foo {
745 bin: usize,
746 bar: usize,
747 baz: usize,
748 }
749 "#,
750 r#"
751 struct Foo {
752 bin: usize,
753 bar: usize,
754 baz: usize,
755 }
756
757 impl PartialOrd for Foo {
758 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
759 match self.bin.partial_cmp(&other.bin) {
760 Some(core::cmp::Ordering::Equal) => {}
761 ord => return ord,
762 }
763 match self.bar.partial_cmp(&other.bar) {
764 Some(core::cmp::Ordering::Equal) => {}
765 ord => return ord,
766 }
767 self.baz.partial_cmp(&other.baz)
768 }
769 }
770 "#,
771 )
772 }
773
774 #[test]
add_custom_impl_partial_ord_tuple_struct()775 fn add_custom_impl_partial_ord_tuple_struct() {
776 check_assist(
777 replace_derive_with_manual_impl,
778 r#"
779 //- minicore: ord, derive
780 #[derive(Partial$0Ord)]
781 struct Foo(usize, usize, usize);
782 "#,
783 r#"
784 struct Foo(usize, usize, usize);
785
786 impl PartialOrd for Foo {
787 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
788 match self.0.partial_cmp(&other.0) {
789 Some(core::cmp::Ordering::Equal) => {}
790 ord => return ord,
791 }
792 match self.1.partial_cmp(&other.1) {
793 Some(core::cmp::Ordering::Equal) => {}
794 ord => return ord,
795 }
796 self.2.partial_cmp(&other.2)
797 }
798 }
799 "#,
800 )
801 }
802
803 #[test]
add_custom_impl_partial_eq_record_struct()804 fn add_custom_impl_partial_eq_record_struct() {
805 check_assist(
806 replace_derive_with_manual_impl,
807 r#"
808 //- minicore: eq, derive
809 #[derive(Partial$0Eq)]
810 struct Foo {
811 bin: usize,
812 bar: usize,
813 }
814 "#,
815 r#"
816 struct Foo {
817 bin: usize,
818 bar: usize,
819 }
820
821 impl PartialEq for Foo {
822 $0fn eq(&self, other: &Self) -> bool {
823 self.bin == other.bin && self.bar == other.bar
824 }
825 }
826 "#,
827 )
828 }
829
830 #[test]
add_custom_impl_partial_eq_tuple_struct()831 fn add_custom_impl_partial_eq_tuple_struct() {
832 check_assist(
833 replace_derive_with_manual_impl,
834 r#"
835 //- minicore: eq, derive
836 #[derive(Partial$0Eq)]
837 struct Foo(usize, usize);
838 "#,
839 r#"
840 struct Foo(usize, usize);
841
842 impl PartialEq for Foo {
843 $0fn eq(&self, other: &Self) -> bool {
844 self.0 == other.0 && self.1 == other.1
845 }
846 }
847 "#,
848 )
849 }
850
851 #[test]
add_custom_impl_partial_eq_empty_struct()852 fn add_custom_impl_partial_eq_empty_struct() {
853 check_assist(
854 replace_derive_with_manual_impl,
855 r#"
856 //- minicore: eq, derive
857 #[derive(Partial$0Eq)]
858 struct Foo;
859 "#,
860 r#"
861 struct Foo;
862
863 impl PartialEq for Foo {
864 $0fn eq(&self, other: &Self) -> bool {
865 true
866 }
867 }
868 "#,
869 )
870 }
871
872 #[test]
add_custom_impl_partial_eq_enum()873 fn add_custom_impl_partial_eq_enum() {
874 check_assist(
875 replace_derive_with_manual_impl,
876 r#"
877 //- minicore: eq, derive
878 #[derive(Partial$0Eq)]
879 enum Foo {
880 Bar,
881 Baz,
882 }
883 "#,
884 r#"
885 enum Foo {
886 Bar,
887 Baz,
888 }
889
890 impl PartialEq for Foo {
891 $0fn eq(&self, other: &Self) -> bool {
892 core::mem::discriminant(self) == core::mem::discriminant(other)
893 }
894 }
895 "#,
896 )
897 }
898
899 #[test]
add_custom_impl_partial_eq_single_variant_tuple_enum()900 fn add_custom_impl_partial_eq_single_variant_tuple_enum() {
901 check_assist(
902 replace_derive_with_manual_impl,
903 r#"
904 //- minicore: eq, derive
905 #[derive(Partial$0Eq)]
906 enum Foo {
907 Bar(String),
908 }
909 "#,
910 r#"
911 enum Foo {
912 Bar(String),
913 }
914
915 impl PartialEq for Foo {
916 $0fn eq(&self, other: &Self) -> bool {
917 match (self, other) {
918 (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
919 }
920 }
921 }
922 "#,
923 )
924 }
925
926 #[test]
add_custom_impl_partial_eq_partial_tuple_enum()927 fn add_custom_impl_partial_eq_partial_tuple_enum() {
928 check_assist(
929 replace_derive_with_manual_impl,
930 r#"
931 //- minicore: eq, derive
932 #[derive(Partial$0Eq)]
933 enum Foo {
934 Bar(String),
935 Baz,
936 }
937 "#,
938 r#"
939 enum Foo {
940 Bar(String),
941 Baz,
942 }
943
944 impl PartialEq for Foo {
945 $0fn eq(&self, other: &Self) -> bool {
946 match (self, other) {
947 (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
948 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
949 }
950 }
951 }
952 "#,
953 )
954 }
955
956 #[test]
add_custom_impl_partial_eq_tuple_enum()957 fn add_custom_impl_partial_eq_tuple_enum() {
958 check_assist(
959 replace_derive_with_manual_impl,
960 r#"
961 //- minicore: eq, derive
962 #[derive(Partial$0Eq)]
963 enum Foo {
964 Bar(String),
965 Baz(i32),
966 }
967 "#,
968 r#"
969 enum Foo {
970 Bar(String),
971 Baz(i32),
972 }
973
974 impl PartialEq for Foo {
975 $0fn eq(&self, other: &Self) -> bool {
976 match (self, other) {
977 (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
978 (Self::Baz(l0), Self::Baz(r0)) => l0 == r0,
979 _ => false,
980 }
981 }
982 }
983 "#,
984 )
985 }
986
987 #[test]
add_custom_impl_partial_eq_tuple_enum_generic()988 fn add_custom_impl_partial_eq_tuple_enum_generic() {
989 check_assist(
990 replace_derive_with_manual_impl,
991 r#"
992 //- minicore: eq, derive
993 #[derive(Partial$0Eq)]
994 enum Either<T, U> {
995 Left(T),
996 Right(U),
997 }
998 "#,
999 r#"
1000 enum Either<T, U> {
1001 Left(T),
1002 Right(U),
1003 }
1004
1005 impl<T: PartialEq, U: PartialEq> PartialEq for Either<T, U> {
1006 $0fn eq(&self, other: &Self) -> bool {
1007 match (self, other) {
1008 (Self::Left(l0), Self::Left(r0)) => l0 == r0,
1009 (Self::Right(l0), Self::Right(r0)) => l0 == r0,
1010 _ => false,
1011 }
1012 }
1013 }
1014 "#,
1015 )
1016 }
1017
1018 #[test]
add_custom_impl_partial_eq_tuple_enum_generic_existing_bounds()1019 fn add_custom_impl_partial_eq_tuple_enum_generic_existing_bounds() {
1020 check_assist(
1021 replace_derive_with_manual_impl,
1022 r#"
1023 //- minicore: eq, derive
1024 #[derive(Partial$0Eq)]
1025 enum Either<T: PartialEq + Error, U: Clone> {
1026 Left(T),
1027 Right(U),
1028 }
1029 "#,
1030 r#"
1031 enum Either<T: PartialEq + Error, U: Clone> {
1032 Left(T),
1033 Right(U),
1034 }
1035
1036 impl<T: PartialEq + Error, U: Clone + PartialEq> PartialEq for Either<T, U> {
1037 $0fn eq(&self, other: &Self) -> bool {
1038 match (self, other) {
1039 (Self::Left(l0), Self::Left(r0)) => l0 == r0,
1040 (Self::Right(l0), Self::Right(r0)) => l0 == r0,
1041 _ => false,
1042 }
1043 }
1044 }
1045 "#,
1046 )
1047 }
1048
1049 #[test]
add_custom_impl_partial_eq_record_enum()1050 fn add_custom_impl_partial_eq_record_enum() {
1051 check_assist(
1052 replace_derive_with_manual_impl,
1053 r#"
1054 //- minicore: eq, derive
1055 #[derive(Partial$0Eq)]
1056 enum Foo {
1057 Bar {
1058 bin: String,
1059 },
1060 Baz {
1061 qux: String,
1062 fez: String,
1063 },
1064 Qux {},
1065 Bin,
1066 }
1067 "#,
1068 r#"
1069 enum Foo {
1070 Bar {
1071 bin: String,
1072 },
1073 Baz {
1074 qux: String,
1075 fez: String,
1076 },
1077 Qux {},
1078 Bin,
1079 }
1080
1081 impl PartialEq for Foo {
1082 $0fn eq(&self, other: &Self) -> bool {
1083 match (self, other) {
1084 (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
1085 (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez,
1086 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
1087 }
1088 }
1089 }
1090 "#,
1091 )
1092 }
1093 #[test]
add_custom_impl_all()1094 fn add_custom_impl_all() {
1095 check_assist(
1096 replace_derive_with_manual_impl,
1097 r#"
1098 //- minicore: derive
1099 mod foo {
1100 pub trait Bar {
1101 type Qux;
1102 const Baz: usize = 42;
1103 const Fez: usize;
1104 fn foo();
1105 fn bar() {}
1106 }
1107 }
1108
1109 #[derive($0Bar)]
1110 struct Foo {
1111 bar: String,
1112 }
1113 "#,
1114 r#"
1115 mod foo {
1116 pub trait Bar {
1117 type Qux;
1118 const Baz: usize = 42;
1119 const Fez: usize;
1120 fn foo();
1121 fn bar() {}
1122 }
1123 }
1124
1125 struct Foo {
1126 bar: String,
1127 }
1128
1129 impl foo::Bar for Foo {
1130 $0type Qux;
1131
1132 const Fez: usize;
1133
1134 fn foo() {
1135 todo!()
1136 }
1137 }
1138 "#,
1139 )
1140 }
1141 #[test]
add_custom_impl_for_unique_input_unknown()1142 fn add_custom_impl_for_unique_input_unknown() {
1143 check_assist(
1144 replace_derive_with_manual_impl,
1145 r#"
1146 //- minicore: derive
1147 #[derive(Debu$0g)]
1148 struct Foo {
1149 bar: String,
1150 }
1151 "#,
1152 r#"
1153 struct Foo {
1154 bar: String,
1155 }
1156
1157 impl Debug for Foo {
1158 $0
1159 }
1160 "#,
1161 )
1162 }
1163
1164 #[test]
add_custom_impl_for_with_visibility_modifier()1165 fn add_custom_impl_for_with_visibility_modifier() {
1166 check_assist(
1167 replace_derive_with_manual_impl,
1168 r#"
1169 //- minicore: derive
1170 #[derive(Debug$0)]
1171 pub struct Foo {
1172 bar: String,
1173 }
1174 "#,
1175 r#"
1176 pub struct Foo {
1177 bar: String,
1178 }
1179
1180 impl Debug for Foo {
1181 $0
1182 }
1183 "#,
1184 )
1185 }
1186
1187 #[test]
add_custom_impl_when_multiple_inputs()1188 fn add_custom_impl_when_multiple_inputs() {
1189 check_assist(
1190 replace_derive_with_manual_impl,
1191 r#"
1192 //- minicore: derive
1193 #[derive(Display, Debug$0, Serialize)]
1194 struct Foo {}
1195 "#,
1196 r#"
1197 #[derive(Display, Serialize)]
1198 struct Foo {}
1199
1200 impl Debug for Foo {
1201 $0
1202 }
1203 "#,
1204 )
1205 }
1206
1207 #[test]
add_custom_impl_default_generic_record_struct()1208 fn add_custom_impl_default_generic_record_struct() {
1209 check_assist(
1210 replace_derive_with_manual_impl,
1211 r#"
1212 //- minicore: default, derive
1213 #[derive(Defau$0lt)]
1214 struct Foo<T, U> {
1215 foo: T,
1216 bar: U,
1217 }
1218 "#,
1219 r#"
1220 struct Foo<T, U> {
1221 foo: T,
1222 bar: U,
1223 }
1224
1225 impl<T: Default, U: Default> Default for Foo<T, U> {
1226 $0fn default() -> Self {
1227 Self { foo: Default::default(), bar: Default::default() }
1228 }
1229 }
1230 "#,
1231 )
1232 }
1233
1234 #[test]
add_custom_impl_clone_generic_tuple_struct_with_bounds()1235 fn add_custom_impl_clone_generic_tuple_struct_with_bounds() {
1236 check_assist(
1237 replace_derive_with_manual_impl,
1238 r#"
1239 //- minicore: clone, derive
1240 #[derive(Clo$0ne)]
1241 struct Foo<T: Clone>(T, usize);
1242 "#,
1243 r#"
1244 struct Foo<T: Clone>(T, usize);
1245
1246 impl<T: Clone> Clone for Foo<T> {
1247 $0fn clone(&self) -> Self {
1248 Self(self.0.clone(), self.1.clone())
1249 }
1250 }
1251 "#,
1252 )
1253 }
1254
1255 #[test]
test_ignore_derive_macro_without_input()1256 fn test_ignore_derive_macro_without_input() {
1257 check_assist_not_applicable(
1258 replace_derive_with_manual_impl,
1259 r#"
1260 //- minicore: derive
1261 #[derive($0)]
1262 struct Foo {}
1263 "#,
1264 )
1265 }
1266
1267 #[test]
test_ignore_if_cursor_on_param()1268 fn test_ignore_if_cursor_on_param() {
1269 check_assist_not_applicable(
1270 replace_derive_with_manual_impl,
1271 r#"
1272 //- minicore: derive, fmt
1273 #[derive$0(Debug)]
1274 struct Foo {}
1275 "#,
1276 );
1277
1278 check_assist_not_applicable(
1279 replace_derive_with_manual_impl,
1280 r#"
1281 //- minicore: derive, fmt
1282 #[derive(Debug)$0]
1283 struct Foo {}
1284 "#,
1285 )
1286 }
1287
1288 #[test]
test_ignore_if_not_derive()1289 fn test_ignore_if_not_derive() {
1290 check_assist_not_applicable(
1291 replace_derive_with_manual_impl,
1292 r#"
1293 //- minicore: derive
1294 #[allow(non_camel_$0case_types)]
1295 struct Foo {}
1296 "#,
1297 )
1298 }
1299
1300 #[test]
works_at_start_of_file()1301 fn works_at_start_of_file() {
1302 check_assist_not_applicable(
1303 replace_derive_with_manual_impl,
1304 r#"
1305 //- minicore: derive, fmt
1306 $0#[derive(Debug)]
1307 struct S;
1308 "#,
1309 );
1310 }
1311
1312 #[test]
add_custom_impl_keep_path()1313 fn add_custom_impl_keep_path() {
1314 check_assist(
1315 replace_derive_with_manual_impl,
1316 r#"
1317 //- minicore: clone, derive
1318 #[derive(std::fmt::Debug, Clo$0ne)]
1319 pub struct Foo;
1320 "#,
1321 r#"
1322 #[derive(std::fmt::Debug)]
1323 pub struct Foo;
1324
1325 impl Clone for Foo {
1326 $0fn clone(&self) -> Self {
1327 Self { }
1328 }
1329 }
1330 "#,
1331 )
1332 }
1333
1334 #[test]
add_custom_impl_replace_path()1335 fn add_custom_impl_replace_path() {
1336 check_assist(
1337 replace_derive_with_manual_impl,
1338 r#"
1339 //- minicore: fmt, derive
1340 #[derive(core::fmt::Deb$0ug, Clone)]
1341 pub struct Foo;
1342 "#,
1343 r#"
1344 #[derive(Clone)]
1345 pub struct Foo;
1346
1347 impl core::fmt::Debug for Foo {
1348 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1349 f.debug_struct("Foo").finish()
1350 }
1351 }
1352 "#,
1353 )
1354 }
1355 }
1356