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