ide_assists/handlers/
replace_derive_with_manual_impl.rs

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
20// Assist: replace_derive_with_manual_impl
21//
22// Converts a `derive` impl into a manual one.
23//
24// ```
25// # //- minicore: derive
26// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
27// #[derive(Deb$0ug, Display)]
28// struct S;
29// ```
30// ->
31// ```
32// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
33// #[derive(Display)]
34// struct S;
35//
36// impl Debug for S {
37//     $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
38//         f.debug_struct("S").finish()
39//     }
40// }
41// ```
42pub(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        // FIXME: make this work in macro files
56        return None;
57    }
58    // collect the derive paths from the #[derive] expansion
59    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            &current_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, &current_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                    // Make the `todo!()` a placeholder
163                    builder.add_placeholder_snippet(cap, m);
164                } else {
165                    // If we haven't already added a snippet, add a tabstop before the generated function
166                    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    // Keep assoc items of local crates even if they have #[doc(hidden)] attr.
196    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        // Make the paths into flat lists of tokens in a vec
254        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        // ...which are interspersed with ", "
260        let tt = Itertools::intersperse(tt, vec![make::token(T![,]), make::tokens::single_space()]);
261        // ...wrap them into the appropriate `NodeOrToken` variant
262        let tt = tt.flatten().map(syntax::NodeOrToken::Token);
263        // ...and make them into a flat list of tokens
264        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        // Remove the attr and any trailing whitespace
270
271        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}