3 get_path_at_cursor_in_tt, import_assets::NameToImport, mod_path_to_ast,
4 parse_tt_as_comma_sep_paths,
6 use ide_db::items_locator;
7 use itertools::Itertools;
9 ast::{self, AstNode, AstToken, HasName},
10 SyntaxKind::WHITESPACE,
14 assist_context::{AssistBuilder, AssistContext, Assists},
16 add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
17 generate_trait_impl_text, render_snippet, Cursor, DefaultMethods,
22 // Assist: replace_derive_with_manual_impl
24 // Converts a `derive` impl into a manual one.
27 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
28 // #[derive(Deb$0ug, Display)]
33 // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
38 // $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
39 // f.debug_struct("S").finish()
43 pub(crate) fn replace_derive_with_manual_impl(
47 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
48 let (name, args) = attr.as_simple_call()?;
53 if !args.syntax().text_range().contains(ctx.offset()) {
54 cov_mark::hit!(outside_of_attr_args);
58 let ident = args.syntax().token_at_offset(ctx.offset()).find_map(ast::Ident::cast)?;
59 let trait_path = get_path_at_cursor_in_tt(&ident)?;
60 let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
62 let current_module = ctx.sema.scope(adt.syntax()).module()?;
63 let current_crate = current_module.krate();
65 let found_traits = items_locator::items_with_name(
68 NameToImport::exact_case_sensitive(trait_path.segments().last()?.to_string()),
69 items_locator::AssocItemSearch::Exclude,
70 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
72 .filter_map(|item| match item.as_module_def()? {
73 ModuleDef::Trait(trait_) => Some(trait_),
78 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
84 let mut no_traits_found = true;
85 let current_derives = parse_tt_as_comma_sep_paths(args.clone())?;
86 let current_derives = current_derives.as_slice();
87 for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
101 add_assist(acc, ctx, &attr, ¤t_derives, &args, &trait_path, &trait_path, None, &adt)?;
110 old_derives: &[ast::Path],
111 old_tree: &ast::TokenTree,
112 old_trait_path: &ast::Path,
113 replace_trait_path: &ast::Path,
114 trait_: Option<hir::Trait>,
117 let target = attr.syntax().text_range();
118 let annotated_name = adt.name()?;
119 let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name);
122 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
126 let insert_pos = adt.syntax().text_range().end();
127 let impl_def_with_items =
128 impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, replace_trait_path);
129 update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
130 let trait_path = format!("{}", replace_trait_path);
131 match (ctx.config.snippet_cap, impl_def_with_items) {
133 builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
135 (Some(cap), None) => builder.insert_snippet(
138 generate_trait_impl_text(adt, &trait_path, " $0"),
140 (Some(cap), Some((impl_def, first_assoc_item))) => {
141 let mut cursor = Cursor::Before(first_assoc_item.syntax());
143 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
144 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
146 if m.syntax().text() == "todo!()" {
148 cursor = Cursor::Replace(placeholder.syntax());
153 builder.insert_snippet(
156 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
164 fn impl_def_from_trait(
165 sema: &hir::Semantics<ide_db::RootDatabase>,
167 annotated_name: &ast::Name,
168 trait_: Option<hir::Trait>,
169 trait_path: &ast::Path,
170 ) -> Option<(ast::Impl, ast::AssocItem)> {
171 let trait_ = trait_?;
172 let target_scope = sema.scope(annotated_name.syntax());
173 let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
174 if trait_items.is_empty() {
178 use syntax::ast::Impl;
179 let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
180 let parse = syntax::SourceFile::parse(&text);
181 let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
185 "Failed to make ast node `{}` from text {}",
186 std::any::type_name::<Impl>(),
191 let node = node.clone_subtree();
192 assert_eq!(node.syntax().text_range().start(), 0.into());
196 let (impl_def, first_assoc_item) =
197 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
199 // Generate a default `impl` function body for the derived trait.
200 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
201 let _ = gen_trait_fn_body(func, trait_path, adt);
204 Some((impl_def, first_assoc_item))
208 builder: &mut AssistBuilder,
209 old_derives: &[ast::Path],
210 old_tree: &ast::TokenTree,
211 old_trait_path: &ast::Path,
214 let new_derives = old_derives
216 .filter(|t| t.to_string() != old_trait_path.to_string())
217 .collect::<Vec<_>>();
218 let has_more_derives = !new_derives.is_empty();
220 if has_more_derives {
221 let new_derives = format!("({})", new_derives.iter().format(", "));
222 builder.replace(old_tree.syntax().text_range(), new_derives);
224 let attr_range = attr.syntax().text_range();
225 builder.delete(attr_range);
227 if let Some(line_break_range) = attr
229 .next_sibling_or_token()
230 .filter(|t| t.kind() == WHITESPACE)
231 .map(|t| t.text_range())
233 builder.delete(line_break_range);
240 use crate::tests::{check_assist, check_assist_not_applicable};
245 fn add_custom_impl_debug_record_struct() {
247 replace_derive_with_manual_impl,
260 impl core::fmt::Debug for Foo {
261 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
262 f.debug_struct("Foo").field("bar", &self.bar).finish()
269 fn add_custom_impl_debug_tuple_struct() {
271 replace_derive_with_manual_impl,
275 struct Foo(String, usize);
277 r#"struct Foo(String, usize);
279 impl core::fmt::Debug for Foo {
280 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281 f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
288 fn add_custom_impl_debug_empty_struct() {
290 replace_derive_with_manual_impl,
299 impl core::fmt::Debug for Foo {
300 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301 f.debug_struct("Foo").finish()
308 fn add_custom_impl_debug_enum() {
310 replace_derive_with_manual_impl,
325 impl core::fmt::Debug for Foo {
326 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
328 Self::Bar => write!(f, "Bar"),
329 Self::Baz => write!(f, "Baz"),
338 fn add_custom_impl_debug_tuple_enum() {
340 replace_derive_with_manual_impl,
355 impl core::fmt::Debug for Foo {
356 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
358 Self::Bar(arg0, arg1) => f.debug_tuple("Bar").field(arg0).field(arg1).finish(),
359 Self::Baz => write!(f, "Baz"),
367 fn add_custom_impl_debug_record_enum() {
369 replace_derive_with_manual_impl,
390 impl core::fmt::Debug for Foo {
391 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
393 Self::Bar { baz, qux } => f.debug_struct("Bar").field("baz", baz).field("qux", qux).finish(),
394 Self::Baz => write!(f, "Baz"),
402 fn add_custom_impl_default_record_struct() {
404 replace_derive_with_manual_impl,
406 //- minicore: default
417 impl Default for Foo {
418 $0fn default() -> Self {
419 Self { foo: Default::default() }
426 fn add_custom_impl_default_tuple_struct() {
428 replace_derive_with_manual_impl,
430 //- minicore: default
437 impl Default for Foo {
438 $0fn default() -> Self {
439 Self(Default::default())
446 fn add_custom_impl_default_empty_struct() {
448 replace_derive_with_manual_impl,
450 //- minicore: default
457 impl Default for Foo {
458 $0fn default() -> Self {
467 fn add_custom_impl_hash_record_struct() {
469 replace_derive_with_manual_impl,
484 impl core::hash::Hash for Foo {
485 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
486 self.bin.hash(state);
487 self.bar.hash(state);
495 fn add_custom_impl_hash_tuple_struct() {
497 replace_derive_with_manual_impl,
501 struct Foo(usize, usize);
504 struct Foo(usize, usize);
506 impl core::hash::Hash for Foo {
507 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
517 fn add_custom_impl_hash_enum() {
519 replace_derive_with_manual_impl,
534 impl core::hash::Hash for Foo {
535 $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
536 core::mem::discriminant(self).hash(state);
544 fn add_custom_impl_clone_record_struct() {
546 replace_derive_with_manual_impl,
562 $0fn clone(&self) -> Self {
563 Self { bin: self.bin.clone(), bar: self.bar.clone() }
571 fn add_custom_impl_clone_tuple_struct() {
573 replace_derive_with_manual_impl,
577 struct Foo(usize, usize);
580 struct Foo(usize, usize);
583 $0fn clone(&self) -> Self {
584 Self(self.0.clone(), self.1.clone())
592 fn add_custom_impl_clone_empty_struct() {
594 replace_derive_with_manual_impl,
604 $0fn clone(&self) -> Self {
613 fn add_custom_impl_clone_enum() {
615 replace_derive_with_manual_impl,
631 $0fn clone(&self) -> Self {
633 Self::Bar => Self::Bar,
634 Self::Baz => Self::Baz,
643 fn add_custom_impl_clone_tuple_enum() {
645 replace_derive_with_manual_impl,
661 $0fn clone(&self) -> Self {
663 Self::Bar(arg0) => Self::Bar(arg0.clone()),
664 Self::Baz => Self::Baz,
673 fn add_custom_impl_clone_record_enum() {
675 replace_derive_with_manual_impl,
695 $0fn clone(&self) -> Self {
697 Self::Bar { bin } => Self::Bar { bin: bin.clone() },
698 Self::Baz => Self::Baz,
707 fn add_custom_impl_partial_ord_record_struct() {
709 replace_derive_with_manual_impl,
712 #[derive(Partial$0Ord)]
722 impl PartialOrd for Foo {
723 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
724 self.bin.partial_cmp(&other.bin)
732 fn add_custom_impl_partial_ord_record_struct_multi_field() {
734 replace_derive_with_manual_impl,
737 #[derive(Partial$0Ord)]
751 impl PartialOrd for Foo {
752 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
753 match self.bin.partial_cmp(&other.bin) {
754 Some(core::cmp::Ordering::Equal) => {}
757 match self.bar.partial_cmp(&other.bar) {
758 Some(core::cmp::Ordering::Equal) => {}
761 self.baz.partial_cmp(&other.baz)
769 fn add_custom_impl_partial_ord_tuple_struct() {
771 replace_derive_with_manual_impl,
774 #[derive(Partial$0Ord)]
775 struct Foo(usize, usize, usize);
778 struct Foo(usize, usize, usize);
780 impl PartialOrd for Foo {
781 $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
782 match self.0.partial_cmp(&other.0) {
783 Some(core::cmp::Ordering::Equal) => {}
786 match self.1.partial_cmp(&other.1) {
787 Some(core::cmp::Ordering::Equal) => {}
790 self.2.partial_cmp(&other.2)
798 fn add_custom_impl_partial_eq_record_struct() {
800 replace_derive_with_manual_impl,
803 #[derive(Partial$0Eq)]
815 impl PartialEq for Foo {
816 $0fn eq(&self, other: &Self) -> bool {
817 self.bin == other.bin && self.bar == other.bar
825 fn add_custom_impl_partial_eq_tuple_struct() {
827 replace_derive_with_manual_impl,
830 #[derive(Partial$0Eq)]
831 struct Foo(usize, usize);
834 struct Foo(usize, usize);
836 impl PartialEq for Foo {
837 $0fn eq(&self, other: &Self) -> bool {
838 self.0 == other.0 && self.1 == other.1
846 fn add_custom_impl_partial_eq_empty_struct() {
848 replace_derive_with_manual_impl,
851 #[derive(Partial$0Eq)]
857 impl PartialEq for Foo {
858 $0fn eq(&self, other: &Self) -> bool {
867 fn add_custom_impl_partial_eq_enum() {
869 replace_derive_with_manual_impl,
872 #[derive(Partial$0Eq)]
884 impl PartialEq for Foo {
885 $0fn eq(&self, other: &Self) -> bool {
886 core::mem::discriminant(self) == core::mem::discriminant(other)
894 fn add_custom_impl_partial_eq_tuple_enum() {
896 replace_derive_with_manual_impl,
899 #[derive(Partial$0Eq)]
911 impl PartialEq for Foo {
912 $0fn eq(&self, other: &Self) -> bool {
913 match (self, other) {
914 (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
915 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
924 fn add_custom_impl_partial_eq_record_enum() {
926 replace_derive_with_manual_impl,
929 #[derive(Partial$0Eq)]
955 impl PartialEq for Foo {
956 $0fn eq(&self, other: &Self) -> bool {
957 match (self, other) {
958 (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
959 (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez,
960 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
968 fn add_custom_impl_all() {
970 replace_derive_with_manual_impl,
975 const Baz: usize = 42;
991 const Baz: usize = 42;
1002 impl foo::Bar for Foo {
1005 const Baz: usize = 42;
1017 fn add_custom_impl_for_unique_input() {
1019 replace_derive_with_manual_impl,
1031 impl Debug for Foo {
1039 fn add_custom_impl_for_with_visibility_modifier() {
1041 replace_derive_with_manual_impl,
1053 impl Debug for Foo {
1061 fn add_custom_impl_when_multiple_inputs() {
1063 replace_derive_with_manual_impl,
1065 #[derive(Display, Debug$0, Serialize)]
1069 #[derive(Display, Serialize)]
1072 impl Debug for Foo {
1080 fn add_custom_impl_default_generic_record_struct() {
1082 replace_derive_with_manual_impl,
1084 //- minicore: default
1085 #[derive(Defau$0lt)]
1097 impl<T, U> Default for Foo<T, U> {
1098 $0fn default() -> Self {
1099 Self { foo: Default::default(), bar: Default::default() }
1107 fn add_custom_impl_clone_generic_tuple_struct_with_bounds() {
1109 replace_derive_with_manual_impl,
1113 struct Foo<T: Clone>(T, usize);
1116 struct Foo<T: Clone>(T, usize);
1118 impl<T: Clone> Clone for Foo<T> {
1119 $0fn clone(&self) -> Self {
1120 Self(self.0.clone(), self.1.clone())
1128 fn test_ignore_derive_macro_without_input() {
1129 check_assist_not_applicable(
1130 replace_derive_with_manual_impl,
1139 fn test_ignore_if_cursor_on_param() {
1140 check_assist_not_applicable(
1141 replace_derive_with_manual_impl,
1148 check_assist_not_applicable(
1149 replace_derive_with_manual_impl,
1158 fn test_ignore_if_not_derive() {
1159 check_assist_not_applicable(
1160 replace_derive_with_manual_impl,
1162 #[allow(non_camel_$0case_types)]
1169 fn works_at_start_of_file() {
1170 cov_mark::check!(outside_of_attr_args);
1171 check_assist_not_applicable(
1172 replace_derive_with_manual_impl,
1181 fn add_custom_impl_keep_path() {
1183 replace_derive_with_manual_impl,
1186 #[derive(std::fmt::Debug, Clo$0ne)]
1190 #[derive(std::fmt::Debug)]
1193 impl Clone for Foo {
1194 $0fn clone(&self) -> Self {
1203 fn add_custom_impl_replace_path() {
1205 replace_derive_with_manual_impl,
1208 #[derive(core::fmt::Deb$0ug, Clone)]
1215 impl core::fmt::Debug for Foo {
1216 $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1217 f.debug_struct("Foo").finish()