From d85abd77b98ff5925621c18f2ffe121640d17c80 Mon Sep 17 00:00:00 2001 From: Kevin DeLorey <2295721+kdelorey@users.noreply.github.com> Date: Sun, 9 Feb 2020 12:24:34 -0600 Subject: [PATCH] Added a utility function that can be used to determine the missing impl items. --- crates/ra_assists/src/lib.rs | 2 +- crates/ra_assists/src/utils.rs | 77 +++++++++- crates/ra_ide/src/completion.rs | 2 +- .../src/completion/complete_trait_impl.rs | 132 +++--------------- 4 files changed, 95 insertions(+), 118 deletions(-) diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 828a8e9e827..cb124eaf0d2 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -9,7 +9,7 @@ mod assist_ctx; mod marks; #[cfg(test)] mod doc_tests; -mod utils; +pub mod utils; pub mod ast_transform; use ra_db::FileRange; diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 0d57222956f..7bc21c6e4e3 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -1,10 +1,83 @@ //! Assorted functions shared by several assists. use ra_syntax::{ - ast::{self, make}, - T, + ast::{self, make, NameOwner}, + AstNode, T, }; +use hir::db::HirDatabase; + +use rustc_hash::FxHashSet; + +pub fn get_missing_impl_items( + db: &impl HirDatabase, + analyzer: &hir::SourceAnalyzer, + impl_block: &ast::ImplBlock, +) -> Vec { + // since the names are unique only to each associated type (fn/type/const), + // create buckets of each already implemented type that we'll use in the + // lookup later. + let mut impl_fns = FxHashSet::default(); + let mut impl_type = FxHashSet::default(); + let mut impl_const = FxHashSet::default(); + + if let Some(item_list) = impl_block.item_list() { + for item in item_list.impl_items() { + match item { + ast::ImplItem::FnDef(f) => { + if let Some(n) = f.name() { + impl_fns.insert(n.syntax().to_string()); + } + } + + ast::ImplItem::TypeAliasDef(t) => { + if let Some(n) = t.name() { + impl_type.insert(n.syntax().to_string()); + } + } + + ast::ImplItem::ConstDef(c) => { + if let Some(n) = c.name() { + impl_const.insert(n.syntax().to_string()); + } + } + } + } + } + + resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { + target_trait + .items(db) + .iter() + .filter(|i| match i { + hir::AssocItem::Function(f) => !impl_fns.contains(&f.name(db).to_string()), + hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), + hir::AssocItem::Const(c) => { + c.name(db).map(|n| !impl_const.contains(&n.to_string())).unwrap_or_default() + } + }) + .map(|i| i.clone()) + .collect() + }) +} + +fn resolve_target_trait( + db: &impl HirDatabase, + analyzer: &hir::SourceAnalyzer, + impl_block: &ast::ImplBlock, +) -> Option { + let ast_path = impl_block + .target_trait() + .map(|it| it.syntax().clone()) + .and_then(ast::PathType::cast)? + .path()?; + + match analyzer.resolve_path(db, &ast_path) { + Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), + _ => None, + } +} + pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { if let Some(expr) = invert_special_case(&expr) { return expr; diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4f24cd1f924..4bdc6ba232b 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -76,6 +76,6 @@ pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option { - let f_name = f.name(ctx.db).to_string(); - - item_list - .impl_items() - .find(|impl_item| { - match impl_item { - ast::ImplItem::FnDef(impl_f) => { - if let Some(n) = impl_f.name() { - f_name == n.syntax().to_string() - } else { - false - } - }, - _ => false - } - }).is_none() - }, - hir::AssocItem::Const(c) => { - let c_name = c.name(ctx.db) - .map(|f| f.to_string()); - - if c_name.is_none() { - return false; - } - - let c_name = c_name.unwrap(); - - item_list - .impl_items() - .find(|impl_item| { - match impl_item { - ast::ImplItem::ConstDef(c) => { - if let Some(n) = c.name() { - c_name == n.syntax().to_string() - } else { - false - } - }, - _ => false - } - }).is_none() - }, - hir::AssocItem::TypeAlias(t) => { - let t_name = t.name(ctx.db).to_string(); - - item_list - .impl_items() - .find(|impl_item| { - match impl_item { - ast::ImplItem::TypeAliasDef(t) => { - if let Some(n) = t.name() { - t_name == n.syntax().to_string() - } else { - false - } - }, - _ => false - } - }).is_none() - } - } - }); - - for item in missing_items { + for item in get_missing_impl_items(ctx.db, &ctx.analyzer, impl_block) { match item { - hir::AssocItem::Function(f) => add_function_impl(acc, ctx, f), - hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, t), - _ => {}, + hir::AssocItem::Function(f) => add_function_impl(acc, ctx, &f), + hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, &t), + _ => {} } } } -fn resolve_target_trait( - db: &impl HirDatabase, - analyzer: &hir::SourceAnalyzer, - impl_block: &ast::ImplBlock -) -> Option { - let ast_path = impl_block - .target_trait() - .map(|it| it.syntax().clone()) - .and_then(ast::PathType::cast)? - .path()?; - - match analyzer.resolve_path(db, &ast_path) { - Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => { - Some(def) - } - _ => None, - } -} - fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir::Function) { use crate::display::FunctionSignature; @@ -144,20 +47,21 @@ fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir: } else { CompletionItemKind::Function }; - + let snippet = { let mut s = format!("{}", display); s.push_str(" {}"); s }; - builder - .insert_text(snippet) - .kind(completion_kind) - .add_to(acc); + builder.insert_text(snippet).kind(completion_kind).add_to(acc); } -fn add_type_alias_impl(acc: &mut Completions, ctx: &CompletionContext, type_alias: &hir::TypeAlias) { +fn add_type_alias_impl( + acc: &mut Completions, + ctx: &CompletionContext, + type_alias: &hir::TypeAlias, +) { let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string()); CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) @@ -290,4 +194,4 @@ mod tests { ] "###); } -} \ No newline at end of file +}