2 use ide_db::helpers::{import_assets::NameToImport, mod_path_to_ast};
3 use ide_db::items_locator;
4 use itertools::Itertools;
6 ast::{self, make, AstNode, HasName},
7 SyntaxKind::{IDENT, WHITESPACE},
11 assist_context::{AssistBuilder, AssistContext, Assists},
13 add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
14 generate_trait_impl_text, render_snippet, Cursor, DefaultMethods,
19 // Assist: replace_derive_with_manual_impl
21 // Converts a `derive` impl into a manual one.
24 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
25 // #[derive(Deb$0ug, Display)]
30 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
35 // $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
36 // f.debug_struct("S").finish()
40 pub(crate) fn replace_derive_with_manual_impl(
44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
45 let (name, args) = attr.as_simple_call()?;
50 if !args.syntax().text_range().contains(ctx.offset()) {
51 cov_mark::hit!(outside_of_attr_args);
55 let trait_token = args.syntax().token_at_offset(ctx.offset()).find(|t| t.kind() == IDENT)?;
56 let trait_name = trait_token.text();
58 let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
60 let current_module = ctx.sema.scope(adt.syntax()).module()?;
61 let current_crate = current_module.krate();
63 let found_traits = items_locator::items_with_name(
66 NameToImport::Exact(trait_name.to_string()),
67 items_locator::AssocItemSearch::Exclude,
68 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
70 .filter_map(|item| match item.as_module_def()? {
71 ModuleDef::Trait(trait_) => Some(trait_),
76 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
82 let mut no_traits_found = true;
83 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?;
87 let trait_path = make::ext::ident_path(trait_name);
88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?;
97 input: &ast::TokenTree,
98 trait_path: &ast::Path,
99 trait_: Option<hir::Trait>,
102 let target = attr.syntax().text_range();
103 let annotated_name = adt.name()?;
104 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name);
105 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
108 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
112 let insert_pos = adt.syntax().text_range().end();
113 let impl_def_with_items =
114 impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, trait_path);
115 update_attribute(builder, input, &trait_name, attr);
116 let trait_path = format!("{}", trait_path);
117 match (ctx.config.snippet_cap, impl_def_with_items) {
119 builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
121 (Some(cap), None) => builder.insert_snippet(
124 generate_trait_impl_text(adt, &trait_path, " $0"),
126 (Some(cap), Some((impl_def, first_assoc_item))) => {
127 let mut cursor = Cursor::Before(first_assoc_item.syntax());
129 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
130 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
132 if m.syntax().text() == "todo!()" {
134 cursor = Cursor::Replace(placeholder.syntax());
139 builder.insert_snippet(
142 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
150 fn impl_def_from_trait(
151 sema: &hir::Semantics<ide_db::RootDatabase>,
153 annotated_name: &ast::Name,
154 trait_: Option<hir::Trait>,
155 trait_path: &ast::Path,
156 ) -> Option<(ast::Impl, ast::AssocItem)> {
157 let trait_ = trait_?;
158 let target_scope = sema.scope(annotated_name.syntax());
159 let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
160 if trait_items.is_empty() {
164 use syntax::ast::Impl;
165 let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
166 let parse = syntax::SourceFile::parse(&text);
167 let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
171 "Failed to make ast node `{}` from text {}",
172 std::any::type_name::<Impl>(),
177 let node = node.clone_subtree();
178 assert_eq!(node.syntax().text_range().start(), 0.into());
182 let (impl_def, first_assoc_item) =
183 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
185 // Generate a default `impl` function body for the derived trait.
186 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
187 let _ = gen_trait_fn_body(func, trait_path, adt);
190 Some((impl_def, first_assoc_item))
194 builder: &mut AssistBuilder,
195 input: &ast::TokenTree,
196 trait_name: &ast::NameRef,
199 let trait_name = trait_name.text();
200 let new_attr_input = input
202 .descendants_with_tokens()
203 .filter(|t| t.kind() == IDENT)
204 .filter_map(|t| t.into_token().map(|t| t.text().to_string()))
205 .filter(|t| t != &trait_name)
206 .collect::<Vec<_>>();
207 let has_more_derives = !new_attr_input.is_empty();
209 if has_more_derives {
210 let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
211 builder.replace(input.syntax().text_range(), new_attr_input);
213 let attr_range = attr.syntax().text_range();
214 builder.delete(attr_range);
216 if let Some(line_break_range) = attr
218 .next_sibling_or_token()
219 .filter(|t| t.kind() == WHITESPACE)
220 .map(|t| t.text_range())
222 builder.delete(line_break_range);
229 use crate::tests::{check_assist, check_assist_not_applicable};
234 fn add_custom_impl_debug_record_struct() {
236 replace_derive_with_manual_impl,
249 impl core::fmt::Debug for Foo {
250 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251 f.debug_struct("Foo").field("bar", &self.bar).finish()
258 fn add_custom_impl_debug_tuple_struct() {
260 replace_derive_with_manual_impl,
264 struct Foo(String, usize);
266 r#"struct Foo(String, usize);
268 impl core::fmt::Debug for Foo {
269 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270 f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
277 fn add_custom_impl_debug_empty_struct() {
279 replace_derive_with_manual_impl,
288 impl core::fmt::Debug for Foo {
289 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
290 f.debug_struct("Foo").finish()
297 fn add_custom_impl_debug_enum() {
299 replace_derive_with_manual_impl,
314 impl core::fmt::Debug for Foo {
315 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
317 Self::Bar => write!(f, "Bar"),
318 Self::Baz => write!(f, "Baz"),
327 fn add_custom_impl_debug_tuple_enum() {
329 replace_derive_with_manual_impl,
344 impl core::fmt::Debug for Foo {
345 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
347 Self::Bar(arg0, arg1) => f.debug_tuple("Bar").field(arg0).field(arg1).finish(),
348 Self::Baz => write!(f, "Baz"),
356 fn add_custom_impl_debug_record_enum() {
358 replace_derive_with_manual_impl,
379 impl core::fmt::Debug for Foo {
380 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
382 Self::Bar { baz, qux } => f.debug_struct("Bar").field("baz", baz).field("qux", qux).finish(),
383 Self::Baz => write!(f, "Baz"),
391 fn add_custom_impl_default_record_struct() {
393 replace_derive_with_manual_impl,
395 //- minicore: default
406 impl Default for Foo {
407 $0fn default() -> Self {
408 Self { foo: Default::default() }
415 fn add_custom_impl_default_tuple_struct() {
417 replace_derive_with_manual_impl,
419 //- minicore: default
426 impl Default for Foo {
427 $0fn default() -> Self {
428 Self(Default::default())
435 fn add_custom_impl_default_empty_struct() {
437 replace_derive_with_manual_impl,
439 //- minicore: default
446 impl Default for Foo {
447 $0fn default() -> Self {
456 fn add_custom_impl_hash_record_struct() {
458 replace_derive_with_manual_impl,
473 impl core::hash::Hash for Foo {
474 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
475 self.bin.hash(state);
476 self.bar.hash(state);
484 fn add_custom_impl_hash_tuple_struct() {
486 replace_derive_with_manual_impl,
490 struct Foo(usize, usize);
493 struct Foo(usize, usize);
495 impl core::hash::Hash for Foo {
496 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
506 fn add_custom_impl_hash_enum() {
508 replace_derive_with_manual_impl,
523 impl core::hash::Hash for Foo {
524 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
525 core::mem::discriminant(self).hash(state);
533 fn add_custom_impl_clone_record_struct() {
535 replace_derive_with_manual_impl,
551 $0fn clone(&self) -> Self {
552 Self { bin: self.bin.clone(), bar: self.bar.clone() }
560 fn add_custom_impl_clone_tuple_struct() {
562 replace_derive_with_manual_impl,
566 struct Foo(usize, usize);
569 struct Foo(usize, usize);
572 $0fn clone(&self) -> Self {
573 Self(self.0.clone(), self.1.clone())
581 fn add_custom_impl_clone_empty_struct() {
583 replace_derive_with_manual_impl,
593 $0fn clone(&self) -> Self {
602 fn add_custom_impl_clone_enum() {
604 replace_derive_with_manual_impl,
620 $0fn clone(&self) -> Self {
622 Self::Bar => Self::Bar,
623 Self::Baz => Self::Baz,
632 fn add_custom_impl_clone_tuple_enum() {
634 replace_derive_with_manual_impl,
650 $0fn clone(&self) -> Self {
652 Self::Bar(arg0) => Self::Bar(arg0.clone()),
653 Self::Baz => Self::Baz,
662 fn add_custom_impl_clone_record_enum() {
664 replace_derive_with_manual_impl,
684 $0fn clone(&self) -> Self {
686 Self::Bar { bin } => Self::Bar { bin: bin.clone() },
687 Self::Baz => Self::Baz,
696 fn add_custom_impl_partial_ord_record_struct() {
698 replace_derive_with_manual_impl,
701 #[derive(Partial$0Ord)]
711 impl PartialOrd for Foo {
712 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
713 self.bin.partial_cmp(&other.bin)
721 fn add_custom_impl_partial_ord_record_struct_multi_field() {
723 replace_derive_with_manual_impl,
726 #[derive(Partial$0Ord)]
740 impl PartialOrd for Foo {
741 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
742 match self.bin.partial_cmp(&other.bin) {
743 Some(core::cmp::Ordering::Equal) => {}
746 match self.bar.partial_cmp(&other.bar) {
747 Some(core::cmp::Ordering::Equal) => {}
750 self.baz.partial_cmp(&other.baz)
758 fn add_custom_impl_partial_ord_tuple_struct() {
760 replace_derive_with_manual_impl,
763 #[derive(Partial$0Ord)]
764 struct Foo(usize, usize, usize);
767 struct Foo(usize, usize, usize);
769 impl PartialOrd for Foo {
770 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
771 match self.0.partial_cmp(&other.0) {
772 Some(core::cmp::Ordering::Equal) => {}
775 match self.1.partial_cmp(&other.1) {
776 Some(core::cmp::Ordering::Equal) => {}
779 self.2.partial_cmp(&other.2)
787 fn add_custom_impl_partial_eq_record_struct() {
789 replace_derive_with_manual_impl,
792 #[derive(Partial$0Eq)]
804 impl PartialEq for Foo {
805 $0fn eq(&self, other: &Self) -> bool {
806 self.bin == other.bin && self.bar == other.bar
814 fn add_custom_impl_partial_eq_tuple_struct() {
816 replace_derive_with_manual_impl,
819 #[derive(Partial$0Eq)]
820 struct Foo(usize, usize);
823 struct Foo(usize, usize);
825 impl PartialEq for Foo {
826 $0fn eq(&self, other: &Self) -> bool {
827 self.0 == other.0 && self.1 == other.1
835 fn add_custom_impl_partial_eq_empty_struct() {
837 replace_derive_with_manual_impl,
840 #[derive(Partial$0Eq)]
846 impl PartialEq for Foo {
847 $0fn eq(&self, other: &Self) -> bool {
856 fn add_custom_impl_partial_eq_enum() {
858 replace_derive_with_manual_impl,
861 #[derive(Partial$0Eq)]
873 impl PartialEq for Foo {
874 $0fn eq(&self, other: &Self) -> bool {
875 core::mem::discriminant(self) == core::mem::discriminant(other)
883 fn add_custom_impl_partial_eq_tuple_enum() {
885 replace_derive_with_manual_impl,
888 #[derive(Partial$0Eq)]
900 impl PartialEq for Foo {
901 $0fn eq(&self, other: &Self) -> bool {
902 match (self, other) {
903 (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
904 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
913 fn add_custom_impl_partial_eq_record_enum() {
915 replace_derive_with_manual_impl,
918 #[derive(Partial$0Eq)]
944 impl PartialEq for Foo {
945 $0fn eq(&self, other: &Self) -> bool {
946 match (self, other) {
947 (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
948 (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez,
949 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
957 fn add_custom_impl_all() {
959 replace_derive_with_manual_impl,
964 const Baz: usize = 42;
980 const Baz: usize = 42;
991 impl foo::Bar for Foo {
994 const Baz: usize = 42;
1006 fn add_custom_impl_for_unique_input() {
1008 replace_derive_with_manual_impl,
1020 impl Debug for Foo {
1028 fn add_custom_impl_for_with_visibility_modifier() {
1030 replace_derive_with_manual_impl,
1042 impl Debug for Foo {
1050 fn add_custom_impl_when_multiple_inputs() {
1052 replace_derive_with_manual_impl,
1054 #[derive(Display, Debug$0, Serialize)]
1058 #[derive(Display, Serialize)]
1061 impl Debug for Foo {
1069 fn add_custom_impl_default_generic_record_struct() {
1071 replace_derive_with_manual_impl,
1073 //- minicore: default
1074 #[derive(Defau$0lt)]
1086 impl<T, U> Default for Foo<T, U> {
1087 $0fn default() -> Self {
1088 Self { foo: Default::default(), bar: Default::default() }
1096 fn add_custom_impl_clone_generic_tuple_struct_with_bounds() {
1098 replace_derive_with_manual_impl,
1102 struct Foo<T: Clone>(T, usize);
1105 struct Foo<T: Clone>(T, usize);
1107 impl<T: Clone> Clone for Foo<T> {
1108 $0fn clone(&self) -> Self {
1109 Self(self.0.clone(), self.1.clone())
1117 fn test_ignore_derive_macro_without_input() {
1118 check_assist_not_applicable(
1119 replace_derive_with_manual_impl,
1128 fn test_ignore_if_cursor_on_param() {
1129 check_assist_not_applicable(
1130 replace_derive_with_manual_impl,
1137 check_assist_not_applicable(
1138 replace_derive_with_manual_impl,
1147 fn test_ignore_if_not_derive() {
1148 check_assist_not_applicable(
1149 replace_derive_with_manual_impl,
1151 #[allow(non_camel_$0case_types)]
1158 fn works_at_start_of_file() {
1159 cov_mark::check!(outside_of_attr_args);
1160 check_assist_not_applicable(
1161 replace_derive_with_manual_impl,