rust/crates/ra_hir/src/impl_block.rs

197 lines
5.8 KiB
Rust
Raw Normal View History

use std::sync::Arc;
use rustc_hash::FxHashMap;
use ra_arena::{Arena, RawId, impl_arena_id};
use ra_syntax::ast::{self, AstNode};
use crate::{
DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
2019-01-24 06:28:50 -06:00
Function, HirFileId,
db::HirDatabase,
type_ref::TypeRef,
2019-01-24 15:26:54 -06:00
ids::LocationCtx,
};
2019-01-06 06:58:45 -06:00
use crate::code_model_api::{Module, ModuleSource};
2019-01-06 06:16:21 -06:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImplBlock {
module_impl_blocks: Arc<ModuleImplBlocks>,
impl_id: ImplId,
}
impl ImplBlock {
pub(crate) fn containing(
module_impl_blocks: Arc<ModuleImplBlocks>,
2019-01-24 06:28:50 -06:00
item: ImplItem,
) -> Option<ImplBlock> {
2019-01-24 06:28:50 -06:00
let impl_id = *module_impl_blocks.impls_by_def.get(&item)?;
Some(ImplBlock {
module_impl_blocks,
impl_id,
})
}
pub(crate) fn from_id(module_impl_blocks: Arc<ModuleImplBlocks>, impl_id: ImplId) -> ImplBlock {
ImplBlock {
module_impl_blocks,
impl_id,
}
}
fn impl_data(&self) -> &ImplData {
&self.module_impl_blocks.impls[self.impl_id]
}
pub fn target_trait(&self) -> Option<&TypeRef> {
self.impl_data().target_trait()
}
pub fn target_type(&self) -> &TypeRef {
self.impl_data().target_type()
}
pub fn items(&self) -> &[ImplItem] {
self.impl_data().items()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImplData {
target_trait: Option<TypeRef>,
target_type: TypeRef,
items: Vec<ImplItem>,
}
impl ImplData {
pub(crate) fn from_ast(
2019-01-24 06:28:50 -06:00
db: &impl HirDatabase,
file_id: HirFileId,
file_items: &SourceFileItems,
module: Module,
2019-01-08 02:28:42 -06:00
node: &ast::ImplBlock,
) -> Self {
let target_trait = node.target_trait().map(TypeRef::from_ast);
let target_type = TypeRef::from_ast_opt(node.target_type());
2019-01-24 15:26:54 -06:00
let ctx = LocationCtx::new(db, module, file_id);
let items = if let Some(item_list) = node.item_list() {
item_list
.impl_items()
.map(|item_node| {
2019-01-08 02:28:42 -06:00
let kind = match item_node.kind() {
2019-01-24 06:31:12 -06:00
ast::ImplItemKind::FnDef(it) => {
2019-01-24 15:26:54 -06:00
return ImplItem::Method(Function { id: ctx.to_def(it) });
2019-01-24 06:31:12 -06:00
}
2019-01-08 02:28:42 -06:00
ast::ImplItemKind::ConstDef(..) => DefKind::Item,
ast::ImplItemKind::TypeDef(..) => DefKind::Item,
};
let item_id = file_items.id_of_unchecked(item_node.syntax());
2019-01-06 06:16:21 -06:00
let source_item_id = SourceItemId {
file_id,
2019-01-06 06:16:21 -06:00
item_id: Some(item_id),
};
let def_loc = DefLoc {
module,
kind,
2019-01-06 06:16:21 -06:00
source_item_id,
};
let def_id = def_loc.id(db);
2019-01-08 02:28:42 -06:00
match item_node.kind() {
2019-01-24 06:31:12 -06:00
ast::ImplItemKind::FnDef(_) => unreachable!(),
2019-01-08 02:28:42 -06:00
ast::ImplItemKind::ConstDef(..) => ImplItem::Const(def_id),
ast::ImplItemKind::TypeDef(..) => ImplItem::Type(def_id),
}
})
.collect()
} else {
Vec::new()
};
ImplData {
target_trait,
target_type,
items,
}
}
pub fn target_trait(&self) -> Option<&TypeRef> {
self.target_trait.as_ref()
}
pub fn target_type(&self) -> &TypeRef {
&self.target_type
}
pub fn items(&self) -> &[ImplItem] {
&self.items
}
}
2019-01-24 06:28:50 -06:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
//TODO: rename to ImplDef?
pub enum ImplItem {
Method(Function),
// these don't have their own types yet
Const(DefId),
Type(DefId),
// Existential
}
2019-01-24 06:28:50 -06:00
impl From<Function> for ImplItem {
fn from(func: Function) -> ImplItem {
ImplItem::Method(func)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ImplId(pub RawId);
impl_arena_id!(ImplId);
/// 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.
#[derive(Debug, PartialEq, Eq)]
pub struct ModuleImplBlocks {
pub(crate) impls: Arena<ImplId, ImplData>,
2019-01-24 06:28:50 -06:00
impls_by_def: FxHashMap<ImplItem, ImplId>,
}
impl ModuleImplBlocks {
fn new() -> Self {
ModuleImplBlocks {
impls: Arena::default(),
impls_by_def: FxHashMap::default(),
}
}
2019-01-15 10:18:52 -06:00
fn collect(&mut self, db: &impl HirDatabase, module: Module) {
2019-01-15 09:26:29 -06:00
let (file_id, module_source) = module.definition_source(db);
let file_id: HirFileId = file_id.into();
2019-01-06 06:58:45 -06:00
let node = match &module_source {
2019-01-08 02:28:42 -06:00
ModuleSource::SourceFile(node) => node.syntax(),
ModuleSource::Module(node) => node
.item_list()
.expect("inline module should have item list")
.syntax(),
};
let source_file_items = db.file_items(file_id);
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
let impl_block =
ImplData::from_ast(db, file_id, &source_file_items, module, impl_block_ast);
let id = self.impls.alloc(impl_block);
2019-01-24 06:28:50 -06:00
for &impl_item in &self.impls[id].items {
self.impls_by_def.insert(impl_item, id);
}
}
}
}
pub(crate) fn impls_in_module(db: &impl HirDatabase, module: Module) -> Arc<ModuleImplBlocks> {
let mut result = ModuleImplBlocks::new();
2019-01-15 10:18:52 -06:00
result.collect(db, module);
Arc::new(result)
}