2018-12-28 13:34:00 +00:00
|
|
|
use rustc_hash::FxHashMap;
|
2019-07-04 20:05:17 +00:00
|
|
|
use std::sync::Arc;
|
2018-12-28 13:34:00 +00:00
|
|
|
|
2019-07-04 20:05:17 +00:00
|
|
|
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
2019-01-28 14:26:32 +00:00
|
|
|
use ra_syntax::{
|
2019-07-04 20:05:17 +00:00
|
|
|
ast::{self, AstNode},
|
2019-09-12 10:12:26 +00:00
|
|
|
AstPtr,
|
2019-03-14 10:14:54 +00:00
|
|
|
};
|
2018-12-28 13:34:00 +00:00
|
|
|
|
|
|
|
use crate::{
|
2019-07-04 20:05:17 +00:00
|
|
|
code_model::{Module, ModuleSource},
|
2019-09-08 06:53:49 +00:00
|
|
|
db::{AstDatabase, DefDatabase, HirDatabase},
|
2019-07-04 20:05:17 +00:00
|
|
|
generics::HasGenericParams,
|
2019-01-24 21:26:54 +00:00
|
|
|
ids::LocationCtx,
|
2019-09-17 19:22:40 +00:00
|
|
|
ids::MacroCallLoc,
|
2019-01-23 22:08:41 +00:00
|
|
|
resolve::Resolver,
|
2019-04-14 11:07:45 +00:00
|
|
|
ty::Ty,
|
2019-07-04 20:05:17 +00:00
|
|
|
type_ref::TypeRef,
|
2019-09-17 19:22:40 +00:00
|
|
|
AssocItem, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef,
|
|
|
|
TypeAlias,
|
2018-12-28 13:34:00 +00:00
|
|
|
};
|
|
|
|
|
2019-01-28 14:26:32 +00:00
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
|
|
pub struct ImplSourceMap {
|
2019-09-17 19:22:40 +00:00
|
|
|
map: ArenaMap<ImplId, Source<AstPtr<ast::ImplBlock>>>,
|
2019-01-28 14:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ImplSourceMap {
|
2019-09-17 19:22:40 +00:00
|
|
|
fn insert(&mut self, impl_id: ImplId, file_id: HirFileId, impl_block: &ast::ImplBlock) {
|
|
|
|
let source = Source { file_id, ast: AstPtr::new(impl_block) };
|
|
|
|
self.map.insert(impl_id, source)
|
2019-01-28 14:26:32 +00:00
|
|
|
}
|
|
|
|
|
2019-09-17 19:22:40 +00:00
|
|
|
pub fn get(&self, db: &impl AstDatabase, impl_id: ImplId) -> Source<ast::ImplBlock> {
|
|
|
|
let src = self.map[impl_id];
|
|
|
|
let root = src.file_syntax(db);
|
|
|
|
src.map(|ptr| ptr.to_node(&root))
|
2019-01-28 14:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-16 20:19:24 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2018-12-28 13:34:00 +00:00
|
|
|
pub struct ImplBlock {
|
2019-02-16 20:09:58 +00:00
|
|
|
module: Module,
|
2018-12-28 13:34:00 +00:00
|
|
|
impl_id: ImplId,
|
|
|
|
}
|
|
|
|
|
2019-06-11 14:36:52 +00:00
|
|
|
impl HasSource for ImplBlock {
|
2019-07-19 07:43:01 +00:00
|
|
|
type Ast = ast::ImplBlock;
|
|
|
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::ImplBlock> {
|
2019-06-11 14:36:52 +00:00
|
|
|
let source_map = db.impls_in_module_with_source_map(self.module).1;
|
2019-09-17 19:22:40 +00:00
|
|
|
source_map.get(db, self.impl_id)
|
2019-06-11 14:36:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 13:34:00 +00:00
|
|
|
impl ImplBlock {
|
|
|
|
pub(crate) fn containing(
|
2019-01-04 18:29:53 +00:00
|
|
|
module_impl_blocks: Arc<ModuleImplBlocks>,
|
2019-09-16 20:01:13 +00:00
|
|
|
item: AssocItem,
|
2018-12-28 13:34:00 +00:00
|
|
|
) -> Option<ImplBlock> {
|
2019-01-24 12:28:50 +00:00
|
|
|
let impl_id = *module_impl_blocks.impls_by_def.get(&item)?;
|
2019-02-16 20:09:58 +00:00
|
|
|
Some(ImplBlock { module: module_impl_blocks.module, impl_id })
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 20:09:58 +00:00
|
|
|
pub(crate) fn from_id(module: Module, impl_id: ImplId) -> ImplBlock {
|
|
|
|
ImplBlock { module, impl_id }
|
2019-01-07 12:44:54 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 20:09:58 +00:00
|
|
|
pub fn id(&self) -> ImplId {
|
|
|
|
self.impl_id
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
2019-01-23 22:08:41 +00:00
|
|
|
pub fn module(&self) -> Module {
|
2019-02-16 20:09:58 +00:00
|
|
|
self.module
|
2019-01-23 22:08:41 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 22:07:26 +00:00
|
|
|
pub fn target_trait(&self, db: &impl DefDatabase) -> Option<TypeRef> {
|
2019-02-16 20:09:58 +00:00
|
|
|
db.impls_in_module(self.module).impls[self.impl_id].target_trait().cloned()
|
2018-12-30 18:59:49 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 12:37:04 +00:00
|
|
|
pub fn target_type(&self, db: &impl DefDatabase) -> TypeRef {
|
2019-02-16 20:09:58 +00:00
|
|
|
db.impls_in_module(self.module).impls[self.impl_id].target_type().clone()
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
2019-01-26 21:52:04 +00:00
|
|
|
pub fn target_ty(&self, db: &impl HirDatabase) -> Ty {
|
2019-02-16 20:09:58 +00:00
|
|
|
Ty::from_hir(db, &self.resolver(db), &self.target_type(db))
|
2019-01-26 21:52:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-26 22:07:26 +00:00
|
|
|
pub fn target_trait_ref(&self, db: &impl HirDatabase) -> Option<TraitRef> {
|
2019-03-31 18:02:16 +00:00
|
|
|
let target_ty = self.target_ty(db);
|
|
|
|
TraitRef::from_hir(db, &self.resolver(db), &self.target_trait(db)?, Some(target_ty))
|
2019-01-26 21:52:04 +00:00
|
|
|
}
|
|
|
|
|
2019-09-16 20:01:13 +00:00
|
|
|
pub fn items(&self, db: &impl DefDatabase) -> Vec<AssocItem> {
|
2019-02-16 20:09:58 +00:00
|
|
|
db.impls_in_module(self.module).impls[self.impl_id].items().to_vec()
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
2019-01-23 22:08:41 +00:00
|
|
|
|
2019-05-07 16:53:16 +00:00
|
|
|
pub fn is_negative(&self, db: &impl DefDatabase) -> bool {
|
|
|
|
db.impls_in_module(self.module).impls[self.impl_id].negative
|
|
|
|
}
|
|
|
|
|
2019-04-14 09:15:11 +00:00
|
|
|
pub(crate) fn resolver(&self, db: &impl DefDatabase) -> Resolver {
|
2019-01-23 22:08:41 +00:00
|
|
|
let r = self.module().resolver(db);
|
2019-02-16 20:21:36 +00:00
|
|
|
// add generic params, if present
|
|
|
|
let p = self.generic_params(db);
|
|
|
|
let r = if !p.params.is_empty() { r.push_generic_params_scope(p) } else { r };
|
2019-01-23 22:08:41 +00:00
|
|
|
let r = r.push_impl_block_scope(self.clone());
|
|
|
|
r
|
|
|
|
}
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct ImplData {
|
2018-12-30 18:59:49 +00:00
|
|
|
target_trait: Option<TypeRef>,
|
|
|
|
target_type: TypeRef,
|
2019-09-16 20:01:13 +00:00
|
|
|
items: Vec<AssocItem>,
|
2019-05-07 16:53:16 +00:00
|
|
|
negative: bool,
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ImplData {
|
|
|
|
pub(crate) fn from_ast(
|
2019-06-01 18:17:57 +00:00
|
|
|
db: &(impl DefDatabase + AstDatabase),
|
2019-01-23 20:14:13 +00:00
|
|
|
file_id: HirFileId,
|
|
|
|
module: Module,
|
2019-01-08 08:28:42 +00:00
|
|
|
node: &ast::ImplBlock,
|
2018-12-28 13:34:00 +00:00
|
|
|
) -> Self {
|
2019-01-07 12:44:54 +00:00
|
|
|
let target_trait = node.target_trait().map(TypeRef::from_ast);
|
2018-12-30 18:59:49 +00:00
|
|
|
let target_type = TypeRef::from_ast_opt(node.target_type());
|
2019-01-24 21:26:54 +00:00
|
|
|
let ctx = LocationCtx::new(db, module, file_id);
|
2019-05-07 16:53:16 +00:00
|
|
|
let negative = node.is_negative();
|
2018-12-28 13:34:00 +00:00
|
|
|
let items = if let Some(item_list) = node.item_list() {
|
|
|
|
item_list
|
|
|
|
.impl_items()
|
2019-08-19 11:04:51 +00:00
|
|
|
.map(|item_node| match item_node {
|
|
|
|
ast::ImplItem::FnDef(it) => Function { id: ctx.to_def(&it) }.into(),
|
|
|
|
ast::ImplItem::ConstDef(it) => Const { id: ctx.to_def(&it) }.into(),
|
|
|
|
ast::ImplItem::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(&it) }.into(),
|
2018-12-28 13:34:00 +00:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
2019-05-07 16:53:16 +00:00
|
|
|
ImplData { target_trait, target_type, items, negative }
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
2019-01-07 12:44:54 +00:00
|
|
|
|
|
|
|
pub fn target_trait(&self) -> Option<&TypeRef> {
|
|
|
|
self.target_trait.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn target_type(&self) -> &TypeRef {
|
|
|
|
&self.target_type
|
|
|
|
}
|
|
|
|
|
2019-09-16 20:01:13 +00:00
|
|
|
pub fn items(&self) -> &[AssocItem] {
|
2019-01-07 12:44:54 +00:00
|
|
|
&self.items
|
|
|
|
}
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub struct ImplId(pub RawId);
|
|
|
|
impl_arena_id!(ImplId);
|
|
|
|
|
2019-01-08 23:47:12 +00:00
|
|
|
/// The collection of impl blocks is a two-step process: first we collect the
|
|
|
|
/// blocks per-module; then we build an index of all impl blocks in the crate.
|
|
|
|
/// This way, we avoid having to do this process for the whole crate whenever
|
|
|
|
/// a file is changed; as long as the impl blocks in the file don't change,
|
|
|
|
/// we don't need to do the second step again.
|
2018-12-28 13:34:00 +00:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2019-01-04 18:29:53 +00:00
|
|
|
pub struct ModuleImplBlocks {
|
2019-02-16 20:09:58 +00:00
|
|
|
pub(crate) module: Module,
|
2019-01-07 12:44:54 +00:00
|
|
|
pub(crate) impls: Arena<ImplId, ImplData>,
|
2019-09-16 20:01:13 +00:00
|
|
|
impls_by_def: FxHashMap<AssocItem, ImplId>,
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
|
2019-01-04 18:29:53 +00:00
|
|
|
impl ModuleImplBlocks {
|
2019-06-01 18:17:57 +00:00
|
|
|
fn collect(
|
|
|
|
db: &(impl DefDatabase + AstDatabase),
|
|
|
|
module: Module,
|
|
|
|
source_map: &mut ImplSourceMap,
|
|
|
|
) -> Self {
|
2019-01-23 22:08:41 +00:00
|
|
|
let mut m = ModuleImplBlocks {
|
|
|
|
module,
|
|
|
|
impls: Arena::default(),
|
|
|
|
impls_by_def: FxHashMap::default(),
|
|
|
|
};
|
|
|
|
|
2019-06-11 14:47:24 +00:00
|
|
|
let src = m.module.definition_source(db);
|
2019-09-17 19:22:40 +00:00
|
|
|
match &src.ast {
|
|
|
|
ModuleSource::SourceFile(node) => {
|
|
|
|
m.collect_from_item_owner(db, source_map, node, src.file_id)
|
|
|
|
}
|
2019-02-08 11:49:43 +00:00
|
|
|
ModuleSource::Module(node) => {
|
2019-09-17 19:22:40 +00:00
|
|
|
let item_list = node.item_list().expect("inline module should have item list");
|
|
|
|
m.collect_from_item_owner(db, source_map, &item_list, src.file_id)
|
2019-02-08 11:49:43 +00:00
|
|
|
}
|
2018-12-28 13:34:00 +00:00
|
|
|
};
|
2019-09-17 19:22:40 +00:00
|
|
|
m
|
|
|
|
}
|
2018-12-28 13:34:00 +00:00
|
|
|
|
2019-09-17 19:22:40 +00:00
|
|
|
fn collect_from_item_owner(
|
|
|
|
&mut self,
|
|
|
|
db: &(impl DefDatabase + AstDatabase),
|
|
|
|
source_map: &mut ImplSourceMap,
|
|
|
|
owner: &dyn ast::ModuleItemOwner,
|
|
|
|
file_id: HirFileId,
|
|
|
|
) {
|
|
|
|
for item in owner.items_with_macros() {
|
|
|
|
match item {
|
|
|
|
ast::ItemOrMacro::Item(ast::ModuleItem::ImplBlock(impl_block_ast)) => {
|
|
|
|
let impl_block = ImplData::from_ast(db, file_id, self.module, &impl_block_ast);
|
|
|
|
let id = self.impls.alloc(impl_block);
|
|
|
|
for &impl_item in &self.impls[id].items {
|
|
|
|
self.impls_by_def.insert(impl_item, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
source_map.insert(id, file_id, &impl_block_ast);
|
|
|
|
}
|
|
|
|
ast::ItemOrMacro::Item(_) => (),
|
|
|
|
ast::ItemOrMacro::Macro(macro_call) => {
|
|
|
|
//FIXME: we should really cut down on the boilerplate required to process a macro
|
|
|
|
let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id);
|
2019-09-26 17:59:38 +00:00
|
|
|
if let Some(path) = macro_call
|
|
|
|
.path()
|
|
|
|
.and_then(|path| Path::from_src(Source { ast: path, file_id }, db))
|
|
|
|
{
|
2019-09-17 19:22:40 +00:00
|
|
|
if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path)
|
|
|
|
{
|
|
|
|
let call_id = MacroCallLoc { def: def.id, ast_id }.id(db);
|
|
|
|
let file_id = call_id.as_file(MacroFileKind::Items);
|
|
|
|
if let Some(item_list) =
|
|
|
|
db.parse_or_expand(file_id).and_then(ast::MacroItems::cast)
|
|
|
|
{
|
|
|
|
self.collect_from_item_owner(db, source_map, &item_list, file_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-28 13:34:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 14:26:32 +00:00
|
|
|
pub(crate) fn impls_in_module_with_source_map_query(
|
2019-06-01 18:17:57 +00:00
|
|
|
db: &(impl DefDatabase + AstDatabase),
|
2019-01-28 14:26:32 +00:00
|
|
|
module: Module,
|
|
|
|
) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
|
|
|
|
let mut source_map = ImplSourceMap::default();
|
|
|
|
|
2019-01-23 22:08:41 +00:00
|
|
|
let result = ModuleImplBlocks::collect(db, module, &mut source_map);
|
2019-01-28 14:26:32 +00:00
|
|
|
(Arc::new(result), Arc::new(source_map))
|
|
|
|
}
|
|
|
|
|
2019-03-23 12:37:04 +00:00
|
|
|
pub(crate) fn impls_in_module(db: &impl DefDatabase, module: Module) -> Arc<ModuleImplBlocks> {
|
2019-01-28 14:26:32 +00:00
|
|
|
db.impls_in_module_with_source_map(module).0
|
|
|
|
}
|