mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
Added a utility function that can be used to determine the missing impl items.
This commit is contained in:
parent
a957c473fd
commit
d85abd77b9
4 changed files with 95 additions and 118 deletions
|
@ -9,7 +9,7 @@ mod assist_ctx;
|
||||||
mod marks;
|
mod marks;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod doc_tests;
|
mod doc_tests;
|
||||||
mod utils;
|
pub mod utils;
|
||||||
pub mod ast_transform;
|
pub mod ast_transform;
|
||||||
|
|
||||||
use ra_db::FileRange;
|
use ra_db::FileRange;
|
||||||
|
|
|
@ -1,10 +1,83 @@
|
||||||
//! Assorted functions shared by several assists.
|
//! Assorted functions shared by several assists.
|
||||||
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, make},
|
ast::{self, make, NameOwner},
|
||||||
T,
|
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<hir::AssocItem> {
|
||||||
|
// 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<hir::Trait> {
|
||||||
|
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 {
|
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
|
||||||
if let Some(expr) = invert_special_case(&expr) {
|
if let Some(expr) = invert_special_case(&expr) {
|
||||||
return expr;
|
return expr;
|
||||||
|
|
|
@ -1,127 +1,30 @@
|
||||||
use crate::completion::{CompletionContext, Completions, CompletionItem, CompletionKind, CompletionItemKind};
|
use crate::completion::{
|
||||||
|
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
|
||||||
|
};
|
||||||
|
|
||||||
use ra_syntax::ast::{self, NameOwner, AstNode};
|
use hir::{self, Docs};
|
||||||
|
|
||||||
use hir::{self, db::HirDatabase, Docs};
|
|
||||||
|
|
||||||
|
use ra_assists::utils::get_missing_impl_items;
|
||||||
|
|
||||||
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let impl_block = ctx.impl_block.as_ref();
|
let impl_block = ctx.impl_block.as_ref();
|
||||||
let item_list = impl_block.and_then(|i| i.item_list());
|
let item_list = impl_block.and_then(|i| i.item_list());
|
||||||
|
|
||||||
if item_list.is_none()
|
if item_list.is_none() || impl_block.is_none() || ctx.function_syntax.is_some() {
|
||||||
|| impl_block.is_none()
|
|
||||||
|| ctx.function_syntax.is_some() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_list = item_list.unwrap();
|
|
||||||
let impl_block = impl_block.unwrap();
|
let impl_block = impl_block.unwrap();
|
||||||
|
|
||||||
let target_trait = resolve_target_trait(ctx.db, &ctx.analyzer, &impl_block);
|
for item in get_missing_impl_items(ctx.db, &ctx.analyzer, impl_block) {
|
||||||
if target_trait.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let target_trait = target_trait.unwrap();
|
|
||||||
|
|
||||||
let trait_items = target_trait.items(ctx.db);
|
|
||||||
let missing_items = trait_items
|
|
||||||
.iter()
|
|
||||||
.filter(|i| {
|
|
||||||
match i {
|
|
||||||
hir::AssocItem::Function(f) => {
|
|
||||||
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 {
|
|
||||||
match item {
|
match item {
|
||||||
hir::AssocItem::Function(f) => add_function_impl(acc, ctx, f),
|
hir::AssocItem::Function(f) => add_function_impl(acc, ctx, &f),
|
||||||
hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, t),
|
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<hir::Trait> {
|
|
||||||
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) {
|
fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir::Function) {
|
||||||
use crate::display::FunctionSignature;
|
use crate::display::FunctionSignature;
|
||||||
|
|
||||||
|
@ -151,13 +54,14 @@ fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir:
|
||||||
s
|
s
|
||||||
};
|
};
|
||||||
|
|
||||||
builder
|
builder.insert_text(snippet).kind(completion_kind).add_to(acc);
|
||||||
.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());
|
let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string());
|
||||||
|
|
||||||
CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
|
CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue