@@ -2,6 +2,8 @@ use hir::ModuleDef;
22use ide_db:: helpers:: { import_assets:: NameToImport , mod_path_to_ast} ;
33use ide_db:: items_locator;
44use itertools:: Itertools ;
5+ use syntax:: ast:: edit:: AstNodeEdit ;
6+ use syntax:: ted;
57use syntax:: {
68 ast:: { self , make, AstNode , NameOwner } ,
79 SyntaxKind :: { IDENT , WHITESPACE } ,
@@ -32,8 +34,8 @@ use crate::{
3234// struct S;
3335//
3436// impl Debug for S {
35- // fn fmt(&self, f: &mut Formatter) -> Result<()> {
36- // ${0:todo!()}
37+ // $0fn fmt(&self, f: &mut Formatter) -> Result<()> {
38+ // f.debug_struct("S").finish()
3739// }
3840// }
3941// ```
@@ -111,7 +113,7 @@ fn add_assist(
111113 |builder| {
112114 let insert_pos = adt. syntax ( ) . text_range ( ) . end ( ) ;
113115 let impl_def_with_items =
114- impl_def_from_trait ( & ctx. sema , & annotated_name, trait_, trait_path) ;
116+ impl_def_from_trait ( & ctx. sema , adt , & annotated_name, trait_, trait_path) ;
115117 update_attribute ( builder, input, & trait_name, attr) ;
116118 let trait_path = format ! ( "{}" , trait_path) ;
117119 match ( ctx. config . snippet_cap , impl_def_with_items) {
@@ -149,6 +151,7 @@ fn add_assist(
149151
150152fn impl_def_from_trait (
151153 sema : & hir:: Semantics < ide_db:: RootDatabase > ,
154+ adt : & ast:: Adt ,
152155 annotated_name : & ast:: Name ,
153156 trait_ : Option < hir:: Trait > ,
154157 trait_path : & ast:: Path ,
@@ -163,9 +166,116 @@ fn impl_def_from_trait(
163166 make:: impl_trait ( trait_path. clone ( ) , make:: ext:: ident_path ( & annotated_name. text ( ) ) ) ;
164167 let ( impl_def, first_assoc_item) =
165168 add_trait_assoc_items_to_impl ( sema, trait_items, trait_, impl_def, target_scope) ;
169+
170+ // Generate a default `impl` function body for the derived trait.
171+ if let ast:: AssocItem :: Fn ( ref func) = first_assoc_item {
172+ let _ = gen_default_impl ( func, trait_path, adt, annotated_name) ;
173+ } ;
174+
166175 Some ( ( impl_def, first_assoc_item) )
167176}
168177
178+ /// Generate custom trait bodies where possible.
179+ ///
180+ /// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
181+ /// `None` means that generating a custom trait body failed, and the body will remain
182+ /// as `todo!` instead.
183+ fn gen_default_impl (
184+ func : & ast:: Fn ,
185+ trait_path : & ast:: Path ,
186+ adt : & ast:: Adt ,
187+ annotated_name : & ast:: Name ,
188+ ) -> Option < ( ) > {
189+ match trait_path. segment ( ) ?. name_ref ( ) ?. text ( ) . as_str ( ) {
190+ "Debug" => gen_debug_impl ( adt, func, annotated_name) ,
191+ _ => Some ( ( ) ) ,
192+ }
193+ }
194+
195+ /// Generate a `Debug` impl based on the fields and members of the target type.
196+ fn gen_debug_impl ( adt : & ast:: Adt , func : & ast:: Fn , annotated_name : & ast:: Name ) -> Option < ( ) > {
197+ match adt {
198+ // `Debug` cannot be derived for unions, so no default impl can be provided.
199+ ast:: Adt :: Union ( _) => Some ( ( ) ) ,
200+
201+ // => match self { Self::Variant => write!(f, "Variant") }
202+ ast:: Adt :: Enum ( enum_) => {
203+ let list = enum_. variant_list ( ) ?;
204+ let mut arms = vec ! [ ] ;
205+ for variant in list. variants ( ) {
206+ let name = variant. name ( ) ?;
207+ let left = make:: ext:: ident_path ( "Self" ) ;
208+ let right = make:: ext:: ident_path ( & format ! ( "{}" , name) ) ;
209+ let variant_name = make:: path_pat ( make:: path_concat ( left, right) ) ;
210+
211+ let target = make:: expr_path ( make:: ext:: ident_path ( "f" ) . into ( ) ) ;
212+ let fmt_string = make:: expr_literal ( & ( format ! ( "\" {}\" " , name) ) ) . into ( ) ;
213+ let args = make:: arg_list ( vec ! [ target, fmt_string] ) ;
214+ let macro_name = make:: expr_path ( make:: ext:: ident_path ( "write" ) ) ;
215+ let macro_call = make:: expr_macro_call ( macro_name, args) ;
216+
217+ arms. push ( make:: match_arm ( Some ( variant_name. into ( ) ) , None , macro_call. into ( ) ) ) ;
218+ }
219+
220+ let match_target = make:: expr_path ( make:: ext:: ident_path ( "self" ) ) ;
221+ let list = make:: match_arm_list ( arms) . indent ( ast:: edit:: IndentLevel ( 1 ) ) ;
222+ let match_expr = make:: expr_match ( match_target, list) ;
223+
224+ let body = make:: block_expr ( None , Some ( match_expr) ) ;
225+ let body = body. indent ( ast:: edit:: IndentLevel ( 1 ) ) ;
226+ ted:: replace ( func. body ( ) ?. syntax ( ) , body. clone_for_update ( ) . syntax ( ) ) ;
227+ Some ( ( ) )
228+ }
229+
230+ ast:: Adt :: Struct ( strukt) => {
231+ let name = format ! ( "\" {}\" " , annotated_name) ;
232+ let args = make:: arg_list ( Some ( make:: expr_literal ( & name) . into ( ) ) ) ;
233+ let target = make:: expr_path ( make:: ext:: ident_path ( "f" ) ) ;
234+
235+ let expr = match strukt. field_list ( ) {
236+ // => f.debug_struct("Name").finish()
237+ None => make:: expr_method_call ( target, make:: name_ref ( "debug_struct" ) , args) ,
238+
239+ // => f.debug_struct("Name").field("foo", &self.foo).finish()
240+ Some ( ast:: FieldList :: RecordFieldList ( field_list) ) => {
241+ let method = make:: name_ref ( "debug_struct" ) ;
242+ let mut expr = make:: expr_method_call ( target, method, args) ;
243+ for field in field_list. fields ( ) {
244+ let name = field. name ( ) ?;
245+ let f_name = make:: expr_literal ( & ( format ! ( "\" {}\" " , name) ) ) . into ( ) ;
246+ let f_path = make:: expr_path ( make:: ext:: ident_path ( "self" ) ) ;
247+ let f_path = make:: expr_ref ( f_path, false ) ;
248+ let f_path = make:: expr_field ( f_path, & format ! ( "{}" , name) ) . into ( ) ;
249+ let args = make:: arg_list ( vec ! [ f_name, f_path] ) ;
250+ expr = make:: expr_method_call ( expr, make:: name_ref ( "field" ) , args) ;
251+ }
252+ expr
253+ }
254+
255+ // => f.debug_tuple("Name").field(self.0).finish()
256+ Some ( ast:: FieldList :: TupleFieldList ( field_list) ) => {
257+ let method = make:: name_ref ( "debug_tuple" ) ;
258+ let mut expr = make:: expr_method_call ( target, method, args) ;
259+ for ( idx, _) in field_list. fields ( ) . enumerate ( ) {
260+ let f_path = make:: expr_path ( make:: ext:: ident_path ( "self" ) ) ;
261+ let f_path = make:: expr_ref ( f_path, false ) ;
262+ let f_path = make:: expr_field ( f_path, & format ! ( "{}" , idx) ) . into ( ) ;
263+ let method = make:: name_ref ( "field" ) ;
264+ expr = make:: expr_method_call ( expr, method, make:: arg_list ( Some ( f_path) ) ) ;
265+ }
266+ expr
267+ }
268+ } ;
269+
270+ let method = make:: name_ref ( "finish" ) ;
271+ let expr = make:: expr_method_call ( expr, method, make:: arg_list ( None ) ) ;
272+ let body = make:: block_expr ( None , Some ( expr) ) . indent ( ast:: edit:: IndentLevel ( 1 ) ) ;
273+ ted:: replace ( func. body ( ) ?. syntax ( ) , body. clone_for_update ( ) . syntax ( ) ) ;
274+ Some ( ( ) )
275+ }
276+ }
277+ }
278+
169279fn update_attribute (
170280 builder : & mut AssistBuilder ,
171281 input : & ast:: TokenTree ,
@@ -207,41 +317,92 @@ mod tests {
207317 use super :: * ;
208318
209319 #[ test]
210- fn add_custom_impl_debug ( ) {
320+ fn add_custom_impl_debug_record_struct ( ) {
211321 check_assist (
212322 replace_derive_with_manual_impl,
213323 r#"
214- mod fmt {
215- pub struct Error;
216- pub type Result = Result<(), Error>;
217- pub struct Formatter<'a>;
218- pub trait Debug {
219- fn fmt(&self, f: &mut Formatter<'_>) -> Result;
220- }
221- }
222-
324+ //- minicore: fmt
223325#[derive(Debu$0g)]
224326struct Foo {
225327 bar: String,
226328}
227329"# ,
228330 r#"
229- mod fmt {
230- pub struct Error;
231- pub type Result = Result<(), Error>;
232- pub struct Formatter<'a>;
233- pub trait Debug {
234- fn fmt(&self, f: &mut Formatter<'_>) -> Result;
331+ struct Foo {
332+ bar: String,
333+ }
334+
335+ impl 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_debug_tuple_struct ( ) {
345+ check_assist (
346+ replace_derive_with_manual_impl,
347+ r#"
348+ //- minicore: fmt
349+ #[derive(Debu$0g)]
350+ struct Foo(String, usize);
351+ "# ,
352+ r#"struct Foo(String, usize);
353+
354+ impl core::fmt::Debug for Foo {
355+ $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
356+ f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
235357 }
236358}
359+ "# ,
360+ )
361+ }
362+ #[ test]
363+ fn add_custom_impl_debug_empty_struct ( ) {
364+ check_assist (
365+ replace_derive_with_manual_impl,
366+ r#"
367+ //- minicore: fmt
368+ #[derive(Debu$0g)]
369+ struct Foo;
370+ "# ,
371+ r#"
372+ struct Foo;
237373
238- struct Foo {
239- bar: String,
374+ impl core::fmt::Debug for Foo {
375+ $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
376+ f.debug_struct("Foo").finish()
377+ }
378+ }
379+ "# ,
380+ )
381+ }
382+ #[ test]
383+ fn add_custom_impl_debug_enum ( ) {
384+ check_assist (
385+ replace_derive_with_manual_impl,
386+ r#"
387+ //- minicore: fmt
388+ #[derive(Debu$0g)]
389+ enum Foo {
390+ Bar,
391+ Baz,
392+ }
393+ "# ,
394+ r#"
395+ enum Foo {
396+ Bar,
397+ Baz,
240398}
241399
242- impl fmt::Debug for Foo {
243- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244- ${0:todo!()}
400+ impl core::fmt::Debug for Foo {
401+ $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
402+ match self {
403+ Self::Bar => write!(f, "Bar"),
404+ Self::Baz => write!(f, "Baz"),
405+ }
245406 }
246407}
247408"# ,
0 commit comments