aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/add_missing_impl_members.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/add_missing_impl_members.rs')
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs608
1 files changed, 608 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
new file mode 100644
index 000000000..448697d31
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -0,0 +1,608 @@
1use hir::{db::HirDatabase, HasSource, InFile};
2use ra_syntax::{
3 ast::{self, edit, make, AstNode, NameOwner},
4 SmolStr,
5};
6
7use crate::{
8 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
9 Assist, AssistCtx, AssistId,
10};
11
12#[derive(PartialEq)]
13enum AddMissingImplMembersMode {
14 DefaultMethodsOnly,
15 NoDefaultMethods,
16}
17
18// Assist: add_impl_missing_members
19//
20// Adds scaffold for required impl members.
21//
22// ```
23// trait Trait<T> {
24// Type X;
25// fn foo(&self) -> T;
26// fn bar(&self) {}
27// }
28//
29// impl Trait<u32> for () {<|>
30//
31// }
32// ```
33// ->
34// ```
35// trait Trait<T> {
36// Type X;
37// fn foo(&self) -> T;
38// fn bar(&self) {}
39// }
40//
41// impl Trait<u32> for () {
42// fn foo(&self) -> u32 { unimplemented!() }
43//
44// }
45// ```
46pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> {
47 add_missing_impl_members_inner(
48 ctx,
49 AddMissingImplMembersMode::NoDefaultMethods,
50 "add_impl_missing_members",
51 "Implement missing members",
52 )
53}
54
55// Assist: add_impl_default_members
56//
57// Adds scaffold for overriding default impl members.
58//
59// ```
60// trait Trait {
61// Type X;
62// fn foo(&self);
63// fn bar(&self) {}
64// }
65//
66// impl Trait for () {
67// Type X = ();
68// fn foo(&self) {}<|>
69//
70// }
71// ```
72// ->
73// ```
74// trait Trait {
75// Type X;
76// fn foo(&self);
77// fn bar(&self) {}
78// }
79//
80// impl Trait for () {
81// Type X = ();
82// fn foo(&self) {}
83// fn bar(&self) {}
84//
85// }
86// ```
87pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
88 add_missing_impl_members_inner(
89 ctx,
90 AddMissingImplMembersMode::DefaultMethodsOnly,
91 "add_impl_default_members",
92 "Implement default members",
93 )
94}
95
96fn add_missing_impl_members_inner(
97 ctx: AssistCtx,
98 mode: AddMissingImplMembersMode,
99 assist_id: &'static str,
100 label: &'static str,
101) -> Option<Assist> {
102 let _p = ra_prof::profile("add_missing_impl_members_inner");
103 let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
104 let impl_item_list = impl_node.item_list()?;
105
106 let (trait_, trait_def) = {
107 let analyzer = ctx.source_analyzer(impl_node.syntax(), None);
108
109 resolve_target_trait_def(ctx.db, &analyzer, &impl_node)?
110 };
111
112 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> {
113 match item {
114 ast::ImplItem::FnDef(def) => def.name(),
115 ast::ImplItem::TypeAliasDef(def) => def.name(),
116 ast::ImplItem::ConstDef(def) => def.name(),
117 }
118 .map(|it| it.text().clone())
119 };
120
121 let trait_items = trait_def.item_list()?.impl_items();
122 let impl_items = impl_item_list.impl_items().collect::<Vec<_>>();
123
124 let missing_items: Vec<_> = trait_items
125 .filter(|t| def_name(t).is_some())
126 .filter(|t| match t {
127 ast::ImplItem::FnDef(def) => match mode {
128 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
129 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
130 },
131 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
132 })
133 .filter(|t| impl_items.iter().all(|i| def_name(i) != def_name(t)))
134 .collect();
135 if missing_items.is_empty() {
136 return None;
137 }
138
139 let db = ctx.db;
140 let file_id = ctx.frange.file_id;
141 let trait_file_id = trait_.source(db).file_id;
142
143 ctx.add_assist(AssistId(assist_id), label, |edit| {
144 let n_existing_items = impl_item_list.impl_items().count();
145 let module = hir::SourceAnalyzer::new(
146 db,
147 hir::InFile::new(file_id.into(), impl_node.syntax()),
148 None,
149 )
150 .module();
151 let ast_transform = QualifyPaths::new(db, module)
152 .or(SubstituteTypeParams::for_trait_impl(db, trait_, impl_node));
153 let items = missing_items
154 .into_iter()
155 .map(|it| ast_transform::apply(&*ast_transform, InFile::new(trait_file_id, it)))
156 .map(|it| match it {
157 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
158 _ => it,
159 })
160 .map(|it| edit::strip_attrs_and_docs(&it));
161 let new_impl_item_list = impl_item_list.append_items(items);
162 let cursor_position = {
163 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap();
164 first_new_item.syntax().text_range().start()
165 };
166
167 edit.replace_ast(impl_item_list, new_impl_item_list);
168 edit.set_cursor(cursor_position);
169 })
170}
171
172fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
173 if fn_def.body().is_none() {
174 fn_def.with_body(make::block_from_expr(make::expr_unimplemented()))
175 } else {
176 fn_def
177 }
178}
179
180/// Given an `ast::ImplBlock`, resolves the target trait (the one being
181/// implemented) to a `ast::TraitDef`.
182fn resolve_target_trait_def(
183 db: &impl HirDatabase,
184 analyzer: &hir::SourceAnalyzer,
185 impl_block: &ast::ImplBlock,
186) -> Option<(hir::Trait, ast::TraitDef)> {
187 let ast_path = impl_block
188 .target_trait()
189 .map(|it| it.syntax().clone())
190 .and_then(ast::PathType::cast)?
191 .path()?;
192
193 match analyzer.resolve_path(db, &ast_path) {
194 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => {
195 Some((def, def.source(db).value))
196 }
197 _ => None,
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::helpers::{check_assist, check_assist_not_applicable};
205
206 #[test]
207 fn test_add_missing_impl_members() {
208 check_assist(
209 add_missing_impl_members,
210 "
211trait Foo {
212 type Output;
213
214 const CONST: usize = 42;
215
216 fn foo(&self);
217 fn bar(&self);
218 fn baz(&self);
219}
220
221struct S;
222
223impl Foo for S {
224 fn bar(&self) {}
225<|>
226}",
227 "
228trait Foo {
229 type Output;
230
231 const CONST: usize = 42;
232
233 fn foo(&self);
234 fn bar(&self);
235 fn baz(&self);
236}
237
238struct S;
239
240impl Foo for S {
241 fn bar(&self) {}
242 <|>type Output;
243 const CONST: usize = 42;
244 fn foo(&self) { unimplemented!() }
245 fn baz(&self) { unimplemented!() }
246
247}",
248 );
249 }
250
251 #[test]
252 fn test_copied_overriden_members() {
253 check_assist(
254 add_missing_impl_members,
255 "
256trait Foo {
257 fn foo(&self);
258 fn bar(&self) -> bool { true }
259 fn baz(&self) -> u32 { 42 }
260}
261
262struct S;
263
264impl Foo for S {
265 fn bar(&self) {}
266<|>
267}",
268 "
269trait Foo {
270 fn foo(&self);
271 fn bar(&self) -> bool { true }
272 fn baz(&self) -> u32 { 42 }
273}
274
275struct S;
276
277impl Foo for S {
278 fn bar(&self) {}
279 <|>fn foo(&self) { unimplemented!() }
280
281}",
282 );
283 }
284
285 #[test]
286 fn test_empty_impl_block() {
287 check_assist(
288 add_missing_impl_members,
289 "
290trait Foo { fn foo(&self); }
291struct S;
292impl Foo for S { <|> }",
293 "
294trait Foo { fn foo(&self); }
295struct S;
296impl Foo for S {
297 <|>fn foo(&self) { unimplemented!() }
298}",
299 );
300 }
301
302 #[test]
303 fn fill_in_type_params_1() {
304 check_assist(
305 add_missing_impl_members,
306 "
307trait Foo<T> { fn foo(&self, t: T) -> &T; }
308struct S;
309impl Foo<u32> for S { <|> }",
310 "
311trait Foo<T> { fn foo(&self, t: T) -> &T; }
312struct S;
313impl Foo<u32> for S {
314 <|>fn foo(&self, t: u32) -> &u32 { unimplemented!() }
315}",
316 );
317 }
318
319 #[test]
320 fn fill_in_type_params_2() {
321 check_assist(
322 add_missing_impl_members,
323 "
324trait Foo<T> { fn foo(&self, t: T) -> &T; }
325struct S;
326impl<U> Foo<U> for S { <|> }",
327 "
328trait Foo<T> { fn foo(&self, t: T) -> &T; }
329struct S;
330impl<U> Foo<U> for S {
331 <|>fn foo(&self, t: U) -> &U { unimplemented!() }
332}",
333 );
334 }
335
336 #[test]
337 fn test_cursor_after_empty_impl_block() {
338 check_assist(
339 add_missing_impl_members,
340 "
341trait Foo { fn foo(&self); }
342struct S;
343impl Foo for S {}<|>",
344 "
345trait Foo { fn foo(&self); }
346struct S;
347impl Foo for S {
348 <|>fn foo(&self) { unimplemented!() }
349}",
350 )
351 }
352
353 #[test]
354 fn test_qualify_path_1() {
355 check_assist(
356 add_missing_impl_members,
357 "
358mod foo {
359 pub struct Bar;
360 trait Foo { fn foo(&self, bar: Bar); }
361}
362struct S;
363impl foo::Foo for S { <|> }",
364 "
365mod foo {
366 pub struct Bar;
367 trait Foo { fn foo(&self, bar: Bar); }
368}
369struct S;
370impl foo::Foo for S {
371 <|>fn foo(&self, bar: foo::Bar) { unimplemented!() }
372}",
373 );
374 }
375
376 #[test]
377 fn test_qualify_path_generic() {
378 check_assist(
379 add_missing_impl_members,
380 "
381mod foo {
382 pub struct Bar<T>;
383 trait Foo { fn foo(&self, bar: Bar<u32>); }
384}
385struct S;
386impl foo::Foo for S { <|> }",
387 "
388mod foo {
389 pub struct Bar<T>;
390 trait Foo { fn foo(&self, bar: Bar<u32>); }
391}
392struct S;
393impl foo::Foo for S {
394 <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() }
395}",
396 );
397 }
398
399 #[test]
400 fn test_qualify_path_and_substitute_param() {
401 check_assist(
402 add_missing_impl_members,
403 "
404mod foo {
405 pub struct Bar<T>;
406 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
407}
408struct S;
409impl foo::Foo<u32> for S { <|> }",
410 "
411mod foo {
412 pub struct Bar<T>;
413 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
414}
415struct S;
416impl foo::Foo<u32> for S {
417 <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() }
418}",
419 );
420 }
421
422 #[test]
423 fn test_substitute_param_no_qualify() {
424 // when substituting params, the substituted param should not be qualified!
425 check_assist(
426 add_missing_impl_members,
427 "
428mod foo {
429 trait Foo<T> { fn foo(&self, bar: T); }
430 pub struct Param;
431}
432struct Param;
433struct S;
434impl foo::Foo<Param> for S { <|> }",
435 "
436mod foo {
437 trait Foo<T> { fn foo(&self, bar: T); }
438 pub struct Param;
439}
440struct Param;
441struct S;
442impl foo::Foo<Param> for S {
443 <|>fn foo(&self, bar: Param) { unimplemented!() }
444}",
445 );
446 }
447
448 #[test]
449 fn test_qualify_path_associated_item() {
450 check_assist(
451 add_missing_impl_members,
452 "
453mod foo {
454 pub struct Bar<T>;
455 impl Bar<T> { type Assoc = u32; }
456 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
457}
458struct S;
459impl foo::Foo for S { <|> }",
460 "
461mod foo {
462 pub struct Bar<T>;
463 impl Bar<T> { type Assoc = u32; }
464 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
465}
466struct S;
467impl foo::Foo for S {
468 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { unimplemented!() }
469}",
470 );
471 }
472
473 #[test]
474 fn test_qualify_path_nested() {
475 check_assist(
476 add_missing_impl_members,
477 "
478mod foo {
479 pub struct Bar<T>;
480 pub struct Baz;
481 trait Foo { fn foo(&self, bar: Bar<Baz>); }
482}
483struct S;
484impl foo::Foo for S { <|> }",
485 "
486mod foo {
487 pub struct Bar<T>;
488 pub struct Baz;
489 trait Foo { fn foo(&self, bar: Bar<Baz>); }
490}
491struct S;
492impl foo::Foo for S {
493 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { unimplemented!() }
494}",
495 );
496 }
497
498 #[test]
499 fn test_qualify_path_fn_trait_notation() {
500 check_assist(
501 add_missing_impl_members,
502 "
503mod foo {
504 pub trait Fn<Args> { type Output; }
505 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
506}
507struct S;
508impl foo::Foo for S { <|> }",
509 "
510mod foo {
511 pub trait Fn<Args> { type Output; }
512 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
513}
514struct S;
515impl foo::Foo for S {
516 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { unimplemented!() }
517}",
518 );
519 }
520
521 #[test]
522 fn test_empty_trait() {
523 check_assist_not_applicable(
524 add_missing_impl_members,
525 "
526trait Foo;
527struct S;
528impl Foo for S { <|> }",
529 )
530 }
531
532 #[test]
533 fn test_ignore_unnamed_trait_members_and_default_methods() {
534 check_assist_not_applicable(
535 add_missing_impl_members,
536 "
537trait Foo {
538 fn (arg: u32);
539 fn valid(some: u32) -> bool { false }
540}
541struct S;
542impl Foo for S { <|> }",
543 )
544 }
545
546 #[test]
547 fn test_with_docstring_and_attrs() {
548 check_assist(
549 add_missing_impl_members,
550 r#"
551#[doc(alias = "test alias")]
552trait Foo {
553 /// doc string
554 type Output;
555
556 #[must_use]
557 fn foo(&self);
558}
559struct S;
560impl Foo for S {}<|>"#,
561 r#"
562#[doc(alias = "test alias")]
563trait Foo {
564 /// doc string
565 type Output;
566
567 #[must_use]
568 fn foo(&self);
569}
570struct S;
571impl Foo for S {
572 <|>type Output;
573 fn foo(&self) { unimplemented!() }
574}"#,
575 )
576 }
577
578 #[test]
579 fn test_default_methods() {
580 check_assist(
581 add_missing_default_members,
582 "
583trait Foo {
584 type Output;
585
586 const CONST: usize = 42;
587
588 fn valid(some: u32) -> bool { false }
589 fn foo(some: u32) -> bool;
590}
591struct S;
592impl Foo for S { <|> }",
593 "
594trait Foo {
595 type Output;
596
597 const CONST: usize = 42;
598
599 fn valid(some: u32) -> bool { false }
600 fn foo(some: u32) -> bool;
601}
602struct S;
603impl Foo for S {
604 <|>fn valid(some: u32) -> bool { false }
605}",
606 )
607 }
608}