From 8d3d509af77756758cea14cc4939d099b4f95993 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 24 Oct 2020 10:47:23 +0300 Subject: Remove dependency on 'assists' from 'completion' crate --- .../src/handlers/add_missing_impl_members.rs | 3 +- .../src/handlers/replace_if_let_with_match.rs | 6 +- .../src/handlers/replace_let_with_if_let.rs | 3 +- .../src/handlers/replace_unwrap_with_match.rs | 3 +- crates/assists/src/utils.rs | 121 +-------------------- crates/completion/Cargo.toml | 1 - crates/completion/src/complete_postfix.rs | 2 +- crates/completion/src/complete_trait_impl.rs | 2 +- crates/ide_db/src/lib.rs | 2 + crates/ide_db/src/traits.rs | 78 +++++++++++++ crates/ide_db/src/ty_filter.rs | 58 ++++++++++ 11 files changed, 151 insertions(+), 128 deletions(-) create mode 100644 crates/ide_db/src/traits.rs create mode 100644 crates/ide_db/src/ty_filter.rs (limited to 'crates') diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index 4c400f287..b82fb30ad 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs @@ -1,4 +1,5 @@ use hir::HasSource; +use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; use syntax::{ ast::{ self, @@ -11,7 +12,7 @@ use syntax::{ use crate::{ assist_context::{AssistContext, Assists}, ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, - utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, + utils::{render_snippet, Cursor}, AssistId, AssistKind, }; diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index 79097621e..9a49c48c1 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs @@ -7,10 +7,8 @@ use syntax::{ AstNode, }; -use crate::{ - utils::{unwrap_trivial_block, TryEnum}, - AssistContext, AssistId, AssistKind, Assists, -}; +use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; +use ide_db::ty_filter::TryEnum; // Assist: replace_if_let_with_match // diff --git a/crates/assists/src/handlers/replace_let_with_if_let.rs b/crates/assists/src/handlers/replace_let_with_if_let.rs index ed6d0c29b..a5bcbda24 100644 --- a/crates/assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/assists/src/handlers/replace_let_with_if_let.rs @@ -9,7 +9,8 @@ use syntax::{ AstNode, T, }; -use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; +use ide_db::ty_filter::TryEnum; // Assist: replace_let_with_if_let // diff --git a/crates/assists/src/handlers/replace_unwrap_with_match.rs b/crates/assists/src/handlers/replace_unwrap_with_match.rs index 4043c219c..f547066f0 100644 --- a/crates/assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/assists/src/handlers/replace_unwrap_with_match.rs @@ -10,9 +10,10 @@ use syntax::{ }; use crate::{ - utils::{render_snippet, Cursor, TryEnum}, + utils::{render_snippet, Cursor}, AssistContext, AssistId, AssistKind, Assists, }; +use ide_db::ty_filter::TryEnum; // Assist: replace_unwrap_with_match // diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 1a6b48b45..56f925ee6 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs @@ -2,14 +2,13 @@ pub(crate) mod insert_use; pub(crate) mod import_assets; -use std::{iter, ops}; +use std::ops; -use hir::{Adt, Crate, Enum, Module, ScopeDef, Semantics, Trait, Type}; +use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; use ide_db::RootDatabase; use itertools::Itertools; -use rustc_hash::FxHashSet; use syntax::{ - ast::{self, make, ArgListOwner, NameOwner}, + ast::{self, make, ArgListOwner}, AstNode, Direction, SyntaxKind::*, SyntaxNode, TextSize, T, @@ -115,72 +114,6 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor } } -pub fn get_missing_assoc_items( - sema: &Semantics, - impl_def: &ast::Impl, -) -> Vec { - // Names must be unique between constants and functions. However, type aliases - // may share the same name as a function or constant. - let mut impl_fns_consts = FxHashSet::default(); - let mut impl_type = FxHashSet::default(); - - if let Some(item_list) = impl_def.assoc_item_list() { - for item in item_list.assoc_items() { - match item { - ast::AssocItem::Fn(f) => { - if let Some(n) = f.name() { - impl_fns_consts.insert(n.syntax().to_string()); - } - } - - ast::AssocItem::TypeAlias(t) => { - if let Some(n) = t.name() { - impl_type.insert(n.syntax().to_string()); - } - } - - ast::AssocItem::Const(c) => { - if let Some(n) = c.name() { - impl_fns_consts.insert(n.syntax().to_string()); - } - } - ast::AssocItem::MacroCall(_) => (), - } - } - } - - resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| { - target_trait - .items(sema.db) - .iter() - .filter(|i| match i { - hir::AssocItem::Function(f) => { - !impl_fns_consts.contains(&f.name(sema.db).to_string()) - } - hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), - hir::AssocItem::Const(c) => c - .name(sema.db) - .map(|n| !impl_fns_consts.contains(&n.to_string())) - .unwrap_or_default(), - }) - .cloned() - .collect() - }) -} - -pub(crate) fn resolve_target_trait( - sema: &Semantics, - impl_def: &ast::Impl, -) -> Option { - let ast_path = - impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?; - - match sema.resolve_path(&ast_path) { - Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), - _ => None, - } -} - pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { node.children_with_tokens() .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) @@ -223,54 +156,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option { } } -#[derive(Clone, Copy)] -pub enum TryEnum { - Result, - Option, -} - -impl TryEnum { - const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; - - pub fn from_ty(sema: &Semantics, ty: &Type) -> Option { - let enum_ = match ty.as_adt() { - Some(Adt::Enum(it)) => it, - _ => return None, - }; - TryEnum::ALL.iter().find_map(|&var| { - if &enum_.name(sema.db).to_string() == var.type_name() { - return Some(var); - } - None - }) - } - - pub(crate) fn happy_case(self) -> &'static str { - match self { - TryEnum::Result => "Ok", - TryEnum::Option => "Some", - } - } - - pub(crate) fn sad_pattern(self) -> ast::Pat { - match self { - TryEnum::Result => make::tuple_struct_pat( - make::path_unqualified(make::path_segment(make::name_ref("Err"))), - iter::once(make::wildcard_pat().into()), - ) - .into(), - TryEnum::Option => make::ident_pat(make::name("None")).into(), - } - } - - fn type_name(self) -> &'static str { - match self { - TryEnum::Result => "Result", - TryEnum::Option => "Option", - } - } -} - /// Helps with finding well-know things inside the standard library. This is /// somewhat similar to the known paths infra inside hir, but it different; We /// want to make sure that IDE specific paths don't become interesting inside diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index 25192456a..8b6e80448 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml @@ -21,7 +21,6 @@ base_db = { path = "../base_db", version = "0.0.0" } ide_db = { path = "../ide_db", version = "0.0.0" } profile = { path = "../profile", version = "0.0.0" } test_utils = { path = "../test_utils", version = "0.0.0" } -assists = { path = "../assists", version = "0.0.0" } call_info = { path = "../call_info", version = "0.0.0" } # completions crate should depend only on the top-level `hir` package. if you need diff --git a/crates/completion/src/complete_postfix.rs b/crates/completion/src/complete_postfix.rs index 700573cf2..2622f12ab 100644 --- a/crates/completion/src/complete_postfix.rs +++ b/crates/completion/src/complete_postfix.rs @@ -2,7 +2,7 @@ mod format_like; -use assists::utils::TryEnum; +use ide_db::ty_filter::TryEnum; use syntax::{ ast::{self, AstNode, AstToken}, TextRange, TextSize, diff --git a/crates/completion/src/complete_trait_impl.rs b/crates/completion/src/complete_trait_impl.rs index c06af99e2..a14be9c73 100644 --- a/crates/completion/src/complete_trait_impl.rs +++ b/crates/completion/src/complete_trait_impl.rs @@ -31,8 +31,8 @@ //! } //! ``` -use assists::utils::get_missing_assoc_items; use hir::{self, HasAttrs, HasSource}; +use ide_db::traits::get_missing_assoc_items; use syntax::{ ast::{self, edit, Impl}, display::function_declaration, diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 7eff247c7..88f74265c 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -10,6 +10,8 @@ pub mod defs; pub mod search; pub mod imports_locator; pub mod source_change; +pub mod ty_filter; +pub mod traits; use std::{fmt, sync::Arc}; diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs new file mode 100644 index 000000000..dcd61a595 --- /dev/null +++ b/crates/ide_db/src/traits.rs @@ -0,0 +1,78 @@ +//! Functionality for obtaining data related to traits from the DB. + +use crate::RootDatabase; +use hir::Semantics; +use rustc_hash::FxHashSet; +use syntax::{ + ast::{self, NameOwner}, + AstNode, +}; + +/// Given the `impl` block, attempts to find the trait this `impl` corresponds to. +pub fn resolve_target_trait( + sema: &Semantics, + impl_def: &ast::Impl, +) -> Option { + let ast_path = + impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?; + + match sema.resolve_path(&ast_path) { + Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), + _ => None, + } +} + +/// Given the `impl` block, returns the list of associated items (e.g. functions or types) that are +/// missing in this `impl` block. +pub fn get_missing_assoc_items( + sema: &Semantics, + impl_def: &ast::Impl, +) -> Vec { + // Names must be unique between constants and functions. However, type aliases + // may share the same name as a function or constant. + let mut impl_fns_consts = FxHashSet::default(); + let mut impl_type = FxHashSet::default(); + + if let Some(item_list) = impl_def.assoc_item_list() { + for item in item_list.assoc_items() { + match item { + ast::AssocItem::Fn(f) => { + if let Some(n) = f.name() { + impl_fns_consts.insert(n.syntax().to_string()); + } + } + + ast::AssocItem::TypeAlias(t) => { + if let Some(n) = t.name() { + impl_type.insert(n.syntax().to_string()); + } + } + + ast::AssocItem::Const(c) => { + if let Some(n) = c.name() { + impl_fns_consts.insert(n.syntax().to_string()); + } + } + ast::AssocItem::MacroCall(_) => (), + } + } + } + + resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| { + target_trait + .items(sema.db) + .iter() + .filter(|i| match i { + hir::AssocItem::Function(f) => { + !impl_fns_consts.contains(&f.name(sema.db).to_string()) + } + hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), + hir::AssocItem::Const(c) => c + .name(sema.db) + .map(|n| !impl_fns_consts.contains(&n.to_string())) + .unwrap_or_default(), + }) + .cloned() + .collect() + }) +} diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs new file mode 100644 index 000000000..63a945282 --- /dev/null +++ b/crates/ide_db/src/ty_filter.rs @@ -0,0 +1,58 @@ +//! This module contains structures for filtering the expected types. +//! Use case for structures in this module is, for example, situation when you need to process +//! only certain `Enum`s. + +use crate::RootDatabase; +use hir::{Adt, Semantics, Type}; +use std::iter; +use syntax::ast::{self, make}; + +/// Enum types that implement `std::ops::Try` trait. +#[derive(Clone, Copy)] +pub enum TryEnum { + Result, + Option, +} + +impl TryEnum { + const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; + + /// Returns `Some(..)` if the provided type is an enum that implements `std::ops::Try`. + pub fn from_ty(sema: &Semantics, ty: &Type) -> Option { + let enum_ = match ty.as_adt() { + Some(Adt::Enum(it)) => it, + _ => return None, + }; + TryEnum::ALL.iter().find_map(|&var| { + if &enum_.name(sema.db).to_string() == var.type_name() { + return Some(var); + } + None + }) + } + + pub fn happy_case(self) -> &'static str { + match self { + TryEnum::Result => "Ok", + TryEnum::Option => "Some", + } + } + + pub fn sad_pattern(self) -> ast::Pat { + match self { + TryEnum::Result => make::tuple_struct_pat( + make::path_unqualified(make::path_segment(make::name_ref("Err"))), + iter::once(make::wildcard_pat().into()), + ) + .into(), + TryEnum::Option => make::ident_pat(make::name("None")).into(), + } + } + + fn type_name(self) -> &'static str { + match self { + TryEnum::Result => "Result", + TryEnum::Option => "Option", + } + } +} -- cgit v1.2.3