From 1c07c5ccf977eb592fd9f4436b8d41d75c115a66 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Wed, 6 Mar 2019 23:45:11 +0100 Subject: Calculate missing functions from impl body --- crates/ra_assists/src/add_missing_impl_members.rs | 63 ++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) 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 @@ +use std::collections::HashSet; + use crate::assist_ctx::{Assist, AssistCtx}; + +use hir::Resolver; use hir::db::HirDatabase; +use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TreeArc}; +use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; +use ra_db::FilePosition; + +/// Given an `ast::ImplBlock`, resolves the target trait (the one being +/// implemented) to a `ast::TraitDef`. +pub(crate) fn resolve_target_trait_def( + db: &impl HirDatabase, + resolver: &Resolver, + impl_block: &ast::ImplBlock, +) -> Option> { + let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?; + let hir_path = ast_path.path().and_then(hir::Path::from_ast)?; + + match resolver.resolve_path(db, &hir_path).take_types() { + Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), + _ => None, + } +} + +pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { + use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE}; + + let node = ctx.covering_node(); + let kinds = node.ancestors().take(3).map(SyntaxNode::kind); + // Only suggest this in `impl Foo for S { [Item...] <|> }` cursor position + if !Iterator::eq(kinds, [WHITESPACE, ITEM_LIST, IMPL_BLOCK].iter().cloned()) { + return None; + } + + let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?; + + let trait_def = { + let db = ctx.db; + // TODO: Can we get the position of cursor itself rather than supplied range? + let range = ctx.frange; + let position = FilePosition { file_id: range.file_id, offset: range.range.start() }; + let resolver = hir::source_binder::resolver_for_position(db, position); + + resolve_target_trait_def(db, &resolver, impl_node)? + }; + + let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None }; + let def_name = |&def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) }; + + let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items(); + let impl_items = impl_node.item_list()?.impl_items(); + + let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::>(); + let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::>(); + + let trait_fn_names = trait_fns.iter().filter_map(def_name).collect::>(); + let impl_fn_names = impl_fns.iter().filter_map(def_name).collect::>(); + + let missing_fn_names = trait_fn_names.difference(&impl_fn_names).collect::>(); + let missing_fns = trait_fns + .iter() + .filter(|&t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false)); -pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx) -> Option { unimplemented!() } -- cgit v1.2.3