@@ -2,6 +2,8 @@ use hir::ModuleDef;
2
2
use ide_db:: helpers:: { import_assets:: NameToImport , mod_path_to_ast} ;
3
3
use ide_db:: items_locator;
4
4
use itertools:: Itertools ;
5
+ use syntax:: ast:: edit:: AstNodeEdit ;
6
+ use syntax:: ted;
5
7
use syntax:: {
6
8
ast:: { self , make, AstNode , NameOwner } ,
7
9
SyntaxKind :: { IDENT , WHITESPACE } ,
@@ -32,8 +34,8 @@ use crate::{
32
34
// struct S;
33
35
//
34
36
// 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()
37
39
// }
38
40
// }
39
41
// ```
@@ -111,7 +113,7 @@ fn add_assist(
111
113
|builder| {
112
114
let insert_pos = adt. syntax ( ) . text_range ( ) . end ( ) ;
113
115
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) ;
115
117
update_attribute ( builder, input, & trait_name, attr) ;
116
118
let trait_path = format ! ( "{}" , trait_path) ;
117
119
match ( ctx. config . snippet_cap , impl_def_with_items) {
@@ -149,6 +151,7 @@ fn add_assist(
149
151
150
152
fn impl_def_from_trait (
151
153
sema : & hir:: Semantics < ide_db:: RootDatabase > ,
154
+ adt : & ast:: Adt ,
152
155
annotated_name : & ast:: Name ,
153
156
trait_ : Option < hir:: Trait > ,
154
157
trait_path : & ast:: Path ,
@@ -163,9 +166,116 @@ fn impl_def_from_trait(
163
166
make:: impl_trait ( trait_path. clone ( ) , make:: ext:: ident_path ( & annotated_name. text ( ) ) ) ;
164
167
let ( impl_def, first_assoc_item) =
165
168
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
+
166
175
Some ( ( impl_def, first_assoc_item) )
167
176
}
168
177
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
+
169
279
fn update_attribute (
170
280
builder : & mut AssistBuilder ,
171
281
input : & ast:: TokenTree ,
@@ -207,41 +317,92 @@ mod tests {
207
317
use super :: * ;
208
318
209
319
#[ test]
210
- fn add_custom_impl_debug ( ) {
320
+ fn add_custom_impl_debug_record_struct ( ) {
211
321
check_assist (
212
322
replace_derive_with_manual_impl,
213
323
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
223
325
#[derive(Debu$0g)]
224
326
struct Foo {
225
327
bar: String,
226
328
}
227
329
"# ,
228
330
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()
235
357
}
236
358
}
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;
237
373
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,
240
398
}
241
399
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
+ }
245
406
}
246
407
}
247
408
"# ,
0 commit comments