2018-12-28 14:34:00 +01:00
|
|
|
use std::sync::Arc;
|
|
|
|
use rustc_hash::FxHashMap;
|
|
|
|
|
|
|
|
use ra_arena::{Arena, RawId, impl_arena_id};
|
|
|
|
use ra_syntax::ast::{self, AstNode};
|
2019-01-04 19:29:53 +01:00
|
|
|
use ra_db::{LocationIntener, Cancelable, SourceRootId};
|
2018-12-28 14:34:00 +01:00
|
|
|
|
|
|
|
use crate::{
|
2019-01-04 19:29:53 +01:00
|
|
|
DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
|
2018-12-28 14:34:00 +01:00
|
|
|
Module, Function,
|
|
|
|
db::HirDatabase,
|
|
|
|
type_ref::TypeRef,
|
2019-01-04 19:29:53 +01:00
|
|
|
module::{ModuleSourceNode, ModuleId},
|
2018-12-28 14:34:00 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct ImplBlock {
|
2019-01-04 19:29:53 +01:00
|
|
|
module_impl_blocks: Arc<ModuleImplBlocks>,
|
2018-12-28 14:34:00 +01:00
|
|
|
impl_id: ImplId,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ImplBlock {
|
|
|
|
pub(crate) fn containing(
|
2019-01-04 19:29:53 +01:00
|
|
|
module_impl_blocks: Arc<ModuleImplBlocks>,
|
2018-12-28 14:34:00 +01:00
|
|
|
def_id: DefId,
|
|
|
|
) -> Option<ImplBlock> {
|
2019-01-04 19:29:53 +01:00
|
|
|
let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?;
|
2018-12-28 14:34:00 +01:00
|
|
|
Some(ImplBlock {
|
2019-01-04 19:29:53 +01:00
|
|
|
module_impl_blocks,
|
2018-12-28 14:34:00 +01:00
|
|
|
impl_id,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn impl_data(&self) -> &ImplData {
|
2019-01-04 19:29:53 +01:00
|
|
|
&self.module_impl_blocks.impls[self.impl_id]
|
2018-12-28 14:34:00 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 19:59:49 +01:00
|
|
|
pub fn target_trait(&self) -> Option<&TypeRef> {
|
|
|
|
self.impl_data().target_trait.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn target_type(&self) -> &TypeRef {
|
|
|
|
&self.impl_data().target_type
|
2018-12-28 14:34:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn items(&self) -> &[ImplItem] {
|
|
|
|
&self.impl_data().items
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct ImplData {
|
2018-12-30 19:59:49 +01:00
|
|
|
target_trait: Option<TypeRef>,
|
|
|
|
target_type: TypeRef,
|
2018-12-28 14:34:00 +01:00
|
|
|
items: Vec<ImplItem>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ImplData {
|
|
|
|
pub(crate) fn from_ast(
|
|
|
|
db: &impl AsRef<LocationIntener<DefLoc, DefId>>,
|
|
|
|
file_items: &SourceFileItems,
|
|
|
|
module: &Module,
|
|
|
|
node: ast::ImplBlock,
|
|
|
|
) -> Self {
|
2018-12-30 19:59:49 +01:00
|
|
|
let target_trait = node.target_type().map(TypeRef::from_ast);
|
|
|
|
let target_type = TypeRef::from_ast_opt(node.target_type());
|
2018-12-28 14:34:00 +01:00
|
|
|
let file_id = module.source().file_id();
|
|
|
|
let items = if let Some(item_list) = node.item_list() {
|
|
|
|
item_list
|
|
|
|
.impl_items()
|
|
|
|
.map(|item_node| {
|
|
|
|
let kind = match item_node {
|
|
|
|
ast::ImplItem::FnDef(..) => DefKind::Function,
|
|
|
|
ast::ImplItem::ConstDef(..) => DefKind::Item,
|
|
|
|
ast::ImplItem::TypeDef(..) => DefKind::Item,
|
|
|
|
};
|
|
|
|
let item_id = file_items.id_of_unchecked(item_node.syntax());
|
|
|
|
let def_loc = DefLoc {
|
|
|
|
kind,
|
|
|
|
source_root_id: module.source_root_id,
|
|
|
|
module_id: module.module_id,
|
|
|
|
source_item_id: SourceItemId {
|
|
|
|
file_id,
|
|
|
|
item_id: Some(item_id),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
let def_id = def_loc.id(db);
|
|
|
|
match item_node {
|
|
|
|
ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)),
|
|
|
|
ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id),
|
|
|
|
ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
2018-12-30 19:59:49 +01:00
|
|
|
ImplData {
|
|
|
|
target_trait,
|
|
|
|
target_type,
|
|
|
|
items,
|
|
|
|
}
|
2018-12-28 14:34:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub enum ImplItem {
|
|
|
|
Method(Function),
|
|
|
|
// these don't have their own types yet
|
|
|
|
Const(DefId),
|
|
|
|
Type(DefId),
|
|
|
|
// Existential
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ImplItem {
|
|
|
|
pub fn def_id(&self) -> DefId {
|
|
|
|
match self {
|
|
|
|
ImplItem::Method(f) => f.def_id(),
|
|
|
|
ImplItem::Const(def_id) => *def_id,
|
|
|
|
ImplItem::Type(def_id) => *def_id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub struct ImplId(pub RawId);
|
|
|
|
impl_arena_id!(ImplId);
|
|
|
|
|
2019-01-04 19:29:53 +01:00
|
|
|
/// 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 someone
|
|
|
|
/// types in any file; as long as the impl blocks in the file don't change, we
|
|
|
|
/// don't need to do the second step again.
|
|
|
|
///
|
|
|
|
/// (The second step does not yet exist currently.)
|
2018-12-28 14:34:00 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2019-01-04 19:29:53 +01:00
|
|
|
pub struct ModuleImplBlocks {
|
2018-12-28 14:34:00 +01:00
|
|
|
impls: Arena<ImplId, ImplData>,
|
|
|
|
impls_by_def: FxHashMap<DefId, ImplId>,
|
|
|
|
}
|
|
|
|
|
2019-01-04 19:29:53 +01:00
|
|
|
impl ModuleImplBlocks {
|
2018-12-28 14:34:00 +01:00
|
|
|
fn new() -> Self {
|
2019-01-04 19:29:53 +01:00
|
|
|
ModuleImplBlocks {
|
2018-12-28 14:34:00 +01:00
|
|
|
impls: Arena::default(),
|
|
|
|
impls_by_def: FxHashMap::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
|
|
|
|
let module_source_node = module.source().resolve(db);
|
|
|
|
let node = match &module_source_node {
|
|
|
|
ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(),
|
|
|
|
ModuleSourceNode::Module(node) => node.borrowed().syntax(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let source_file_items = db.file_items(module.source().file_id());
|
|
|
|
|
|
|
|
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
|
|
|
let impl_block = ImplData::from_ast(db, &source_file_items, &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.def_id(), id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 19:29:53 +01:00
|
|
|
pub(crate) fn impls_in_module(
|
2018-12-28 14:34:00 +01:00
|
|
|
db: &impl HirDatabase,
|
2019-01-04 19:29:53 +01:00
|
|
|
source_root_id: SourceRootId,
|
|
|
|
module_id: ModuleId,
|
|
|
|
) -> Cancelable<Arc<ModuleImplBlocks>> {
|
|
|
|
let mut result = ModuleImplBlocks::new();
|
|
|
|
let module = Module::new(db, source_root_id, module_id)?;
|
|
|
|
result.collect(db, module)?;
|
2018-12-28 14:34:00 +01:00
|
|
|
Ok(Arc::new(result))
|
|
|
|
}
|