aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Matuszewski <[email protected]>2019-03-06 22:45:11 +0000
committerIgor Matuszewski <[email protected]>2019-03-16 21:41:13 +0000
commit1c07c5ccf977eb592fd9f4436b8d41d75c115a66 (patch)
tree72cc141cb66d4dd49e20c84ded1556d9426529e5
parentfc060573f9374af3b3a44343d303ef6e26f116a8 (diff)
Calculate missing functions from impl body
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs63
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 @@
1use std::collections::HashSet;
2
1use crate::assist_ctx::{Assist, AssistCtx}; 3use crate::assist_ctx::{Assist, AssistCtx};
4
5use hir::Resolver;
2use hir::db::HirDatabase; 6use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TreeArc};
8use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
9use ra_db::FilePosition;
10
11/// Given an `ast::ImplBlock`, resolves the target trait (the one being
12/// implemented) to a `ast::TraitDef`.
13pub(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
27pub(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
4pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
5 unimplemented!() 66 unimplemented!()
6} 67}
7 68