diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 63 |
1 files changed, 62 insertions, 1 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index a0b656f8f..120109d4b 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs | |||
@@ -1,7 +1,68 @@ | |||
1 | use std::collections::HashSet; | ||
2 | |||
1 | use crate::assist_ctx::{Assist, AssistCtx}; | 3 | use crate::assist_ctx::{Assist, AssistCtx}; |
4 | |||
5 | use hir::Resolver; | ||
2 | use hir::db::HirDatabase; | 6 | use hir::db::HirDatabase; |
7 | use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TreeArc}; | ||
8 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; | ||
9 | use ra_db::FilePosition; | ||
10 | |||
11 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | ||
12 | /// implemented) to a `ast::TraitDef`. | ||
13 | pub(crate) fn resolve_target_trait_def( | ||
14 | db: &impl HirDatabase, | ||
15 | resolver: &Resolver, | ||
16 | impl_block: &ast::ImplBlock, | ||
17 | ) -> Option<TreeArc<ast::TraitDef>> { | ||
18 | let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?; | ||
19 | let hir_path = ast_path.path().and_then(hir::Path::from_ast)?; | ||
20 | |||
21 | match resolver.resolve_path(db, &hir_path).take_types() { | ||
22 | Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), | ||
23 | _ => None, | ||
24 | } | ||
25 | } | ||
26 | |||
27 | pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
28 | use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE}; | ||
29 | |||
30 | let node = ctx.covering_node(); | ||
31 | let kinds = node.ancestors().take(3).map(SyntaxNode::kind); | ||
32 | // Only suggest this in `impl Foo for S { [Item...] <|> }` cursor position | ||
33 | if !Iterator::eq(kinds, [WHITESPACE, ITEM_LIST, IMPL_BLOCK].iter().cloned()) { | ||
34 | return None; | ||
35 | } | ||
36 | |||
37 | let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?; | ||
38 | |||
39 | let trait_def = { | ||
40 | let db = ctx.db; | ||
41 | // TODO: Can we get the position of cursor itself rather than supplied range? | ||
42 | let range = ctx.frange; | ||
43 | let position = FilePosition { file_id: range.file_id, offset: range.range.start() }; | ||
44 | let resolver = hir::source_binder::resolver_for_position(db, position); | ||
45 | |||
46 | resolve_target_trait_def(db, &resolver, impl_node)? | ||
47 | }; | ||
48 | |||
49 | let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None }; | ||
50 | let def_name = |&def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) }; | ||
51 | |||
52 | let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items(); | ||
53 | let impl_items = impl_node.item_list()?.impl_items(); | ||
54 | |||
55 | let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>(); | ||
56 | let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>(); | ||
57 | |||
58 | let trait_fn_names = trait_fns.iter().filter_map(def_name).collect::<HashSet<_>>(); | ||
59 | let impl_fn_names = impl_fns.iter().filter_map(def_name).collect::<HashSet<_>>(); | ||
60 | |||
61 | let missing_fn_names = trait_fn_names.difference(&impl_fn_names).collect::<HashSet<_>>(); | ||
62 | let missing_fns = trait_fns | ||
63 | .iter() | ||
64 | .filter(|&t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false)); | ||
3 | 65 | ||
4 | pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
5 | unimplemented!() | 66 | unimplemented!() |
6 | } | 67 | } |
7 | 68 | ||