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