2019-01-18 11:34:13 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use ra_syntax::{
|
2019-01-23 15:26:02 +00:00
|
|
|
SyntaxKind, AstNode, SourceFile, TreeArc, AstPtr,
|
2019-01-23 16:49:11 +00:00
|
|
|
ast::{self, ModuleItemOwner, NameOwner},
|
2019-01-18 11:34:13 +00:00
|
|
|
};
|
2019-01-18 13:36:56 +00:00
|
|
|
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
|
2019-01-23 16:49:11 +00:00
|
|
|
use rustc_hash::FxHashMap;
|
2019-01-18 11:34:13 +00:00
|
|
|
|
|
|
|
use crate::{
|
2019-01-18 13:56:02 +00:00
|
|
|
SourceItemId, Path, ModuleSource, HirDatabase, Name, SourceFileItems,
|
2019-01-24 12:28:50 +00:00
|
|
|
HirFileId, MacroCallLoc, AsName, PerNs, DefKind, DefLoc, Function,
|
2019-01-24 15:56:38 +00:00
|
|
|
ModuleDef, Module, Struct, Enum,
|
2019-01-24 21:26:54 +00:00
|
|
|
ids::LocationCtx,
|
2019-01-18 11:34:13 +00:00
|
|
|
};
|
|
|
|
|
2019-01-18 13:36:56 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2019-01-18 13:56:02 +00:00
|
|
|
pub struct ImportId(RawId);
|
|
|
|
impl_arena_id!(ImportId);
|
2019-01-18 13:36:56 +00:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
pub(super) struct ImportData {
|
|
|
|
pub(super) path: Path,
|
|
|
|
pub(super) is_glob: bool,
|
|
|
|
}
|
|
|
|
|
2019-01-18 13:56:02 +00:00
|
|
|
/// A set of items and imports declared inside a module, without relation to
|
|
|
|
/// other modules.
|
|
|
|
///
|
|
|
|
/// This sits in-between raw syntax and name resolution and allows us to avoid
|
|
|
|
/// recomputing name res: if two instance of `InputModuleItems` are the same, we
|
|
|
|
/// can avoid redoing name resolution.
|
2019-01-18 13:36:56 +00:00
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
|
|
pub struct LoweredModule {
|
2019-01-23 20:14:13 +00:00
|
|
|
pub(crate) declarations: FxHashMap<Name, PerNs<ModuleDef>>,
|
2019-01-18 13:56:02 +00:00
|
|
|
pub(super) imports: Arena<ImportId, ImportData>,
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
|
|
pub struct ImportSourceMap {
|
2019-01-23 15:26:02 +00:00
|
|
|
map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ImportSourceMap {
|
2019-01-18 13:56:02 +00:00
|
|
|
fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
|
2019-01-23 15:26:02 +00:00
|
|
|
self.map.insert(import, AstPtr::new(segment))
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
|
2019-01-18 13:56:02 +00:00
|
|
|
pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
|
2019-01-18 13:36:56 +00:00
|
|
|
let file = match source {
|
|
|
|
ModuleSource::SourceFile(file) => &*file,
|
|
|
|
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
|
|
|
};
|
|
|
|
|
2019-01-23 15:26:02 +00:00
|
|
|
self.map[import].to_node(file).to_owned()
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LoweredModule {
|
|
|
|
pub(crate) fn lower_module_module_query(
|
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-18 13:36:56 +00:00
|
|
|
) -> Arc<LoweredModule> {
|
2019-01-23 20:14:13 +00:00
|
|
|
db.lower_module(module).0
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn lower_module_source_map_query(
|
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-18 13:36:56 +00:00
|
|
|
) -> Arc<ImportSourceMap> {
|
2019-01-23 20:14:13 +00:00
|
|
|
db.lower_module(module).1
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn lower_module_query(
|
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-18 13:36:56 +00:00
|
|
|
) -> (Arc<LoweredModule>, Arc<ImportSourceMap>) {
|
2019-01-23 20:14:13 +00:00
|
|
|
let (file_id, source) = module.definition_source(db);
|
|
|
|
let file_id: HirFileId = file_id.into();
|
2019-01-18 13:36:56 +00:00
|
|
|
let mut source_map = ImportSourceMap::default();
|
|
|
|
let mut res = LoweredModule::default();
|
|
|
|
match source {
|
|
|
|
ModuleSource::SourceFile(it) => res.fill(
|
|
|
|
&mut source_map,
|
|
|
|
db,
|
2019-01-23 20:14:13 +00:00
|
|
|
module,
|
2019-01-18 13:36:56 +00:00
|
|
|
file_id,
|
|
|
|
&mut it.items_with_macros(),
|
|
|
|
),
|
|
|
|
ModuleSource::Module(it) => {
|
|
|
|
if let Some(item_list) = it.item_list() {
|
|
|
|
res.fill(
|
|
|
|
&mut source_map,
|
|
|
|
db,
|
2019-01-23 20:14:13 +00:00
|
|
|
module,
|
2019-01-18 13:36:56 +00:00
|
|
|
file_id,
|
|
|
|
&mut item_list.items_with_macros(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
(Arc::new(res), Arc::new(source_map))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fill(
|
|
|
|
&mut self,
|
|
|
|
source_map: &mut ImportSourceMap,
|
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-18 13:36:56 +00:00
|
|
|
file_id: HirFileId,
|
|
|
|
items: &mut Iterator<Item = ast::ItemOrMacro>,
|
|
|
|
) {
|
|
|
|
let file_items = db.file_items(file_id);
|
|
|
|
|
|
|
|
for item in items {
|
|
|
|
match item {
|
|
|
|
ast::ItemOrMacro::Item(it) => {
|
2019-01-23 20:14:13 +00:00
|
|
|
self.add_def_id(source_map, db, module, file_id, &file_items, it);
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
ast::ItemOrMacro::Macro(macro_call) => {
|
|
|
|
let item_id = file_items.id_of_unchecked(macro_call.syntax());
|
|
|
|
let loc = MacroCallLoc {
|
2019-01-23 20:14:13 +00:00
|
|
|
module,
|
2019-01-18 13:36:56 +00:00
|
|
|
source_item_id: SourceItemId {
|
|
|
|
file_id,
|
|
|
|
item_id: Some(item_id),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
let id = loc.id(db);
|
|
|
|
let file_id = HirFileId::from(id);
|
|
|
|
let file_items = db.file_items(file_id);
|
|
|
|
//FIXME: expand recursively
|
|
|
|
for item in db.hir_source_file(file_id).items() {
|
2019-01-23 20:14:13 +00:00
|
|
|
self.add_def_id(source_map, db, module, file_id, &file_items, item);
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-23 16:49:11 +00:00
|
|
|
fn add_def_id(
|
2019-01-18 13:36:56 +00:00
|
|
|
&mut self,
|
|
|
|
source_map: &mut ImportSourceMap,
|
2019-01-23 16:49:11 +00:00
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-18 13:36:56 +00:00
|
|
|
file_id: HirFileId,
|
|
|
|
file_items: &SourceFileItems,
|
|
|
|
item: &ast::ModuleItem,
|
2019-01-23 16:49:11 +00:00
|
|
|
) {
|
2019-01-24 21:26:54 +00:00
|
|
|
let ctx = LocationCtx::new(db, module, file_id);
|
2019-01-23 16:49:11 +00:00
|
|
|
let name = match item.kind() {
|
2019-01-24 14:54:18 +00:00
|
|
|
ast::ModuleItemKind::StructDef(it) => {
|
|
|
|
if let Some(name) = it.name() {
|
2019-01-24 21:26:54 +00:00
|
|
|
let s = Struct { id: ctx.to_def(it) };
|
2019-01-24 14:54:18 +00:00
|
|
|
let s: ModuleDef = s.into();
|
|
|
|
self.declarations.insert(name.as_name(), PerNs::both(s, s));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2019-01-24 15:56:38 +00:00
|
|
|
ast::ModuleItemKind::EnumDef(it) => {
|
|
|
|
if let Some(name) = it.name() {
|
2019-01-24 21:26:54 +00:00
|
|
|
let e = Enum { id: ctx.to_def(it) };
|
2019-01-24 15:56:38 +00:00
|
|
|
let e: ModuleDef = e.into();
|
|
|
|
self.declarations.insert(name.as_name(), PerNs::types(e));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2019-01-24 12:28:50 +00:00
|
|
|
ast::ModuleItemKind::FnDef(it) => {
|
|
|
|
if let Some(name) = it.name() {
|
2019-01-24 21:26:54 +00:00
|
|
|
let func = Function { id: ctx.to_def(it) };
|
2019-01-24 12:28:50 +00:00
|
|
|
self.declarations
|
|
|
|
.insert(name.as_name(), PerNs::values(func.into()));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2019-01-23 16:49:11 +00:00
|
|
|
ast::ModuleItemKind::TraitDef(it) => it.name(),
|
|
|
|
ast::ModuleItemKind::TypeDef(it) => it.name(),
|
2019-01-18 13:36:56 +00:00
|
|
|
ast::ModuleItemKind::ImplBlock(_) => {
|
|
|
|
// impls don't define items
|
2019-01-23 16:49:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ast::ModuleItemKind::UseItem(it) => {
|
|
|
|
self.add_use_item(source_map, it);
|
|
|
|
return;
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
ast::ModuleItemKind::ExternCrateItem(_) => {
|
|
|
|
// TODO
|
2019-01-23 16:49:11 +00:00
|
|
|
return;
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
2019-01-23 16:49:11 +00:00
|
|
|
ast::ModuleItemKind::ConstDef(it) => it.name(),
|
|
|
|
ast::ModuleItemKind::StaticDef(it) => it.name(),
|
|
|
|
ast::ModuleItemKind::Module(_) => {
|
|
|
|
// modules are handled separately direclty by nameres
|
|
|
|
return;
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
2019-01-23 16:49:11 +00:00
|
|
|
};
|
|
|
|
if let Some(name) = name {
|
2019-01-23 20:14:13 +00:00
|
|
|
let def_id = assign_def_id(db, module, file_id, file_items, item);
|
2019-01-23 16:49:11 +00:00
|
|
|
self.declarations.insert(name.as_name(), def_id);
|
2019-01-18 13:36:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
|
|
|
|
Path::expand_use_item(item, |path, segment| {
|
|
|
|
let import = self.imports.alloc(ImportData {
|
|
|
|
path,
|
|
|
|
is_glob: segment.is_none(),
|
|
|
|
});
|
|
|
|
if let Some(segment) = segment {
|
|
|
|
source_map.insert(import, segment)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2019-01-23 16:49:11 +00:00
|
|
|
|
|
|
|
fn assign_def_id(
|
|
|
|
db: &impl HirDatabase,
|
2019-01-23 20:14:13 +00:00
|
|
|
module: Module,
|
2019-01-23 16:49:11 +00:00
|
|
|
file_id: HirFileId,
|
|
|
|
file_items: &SourceFileItems,
|
|
|
|
item: &ast::ModuleItem,
|
2019-01-23 20:14:13 +00:00
|
|
|
) -> PerNs<ModuleDef> {
|
2019-01-23 16:49:11 +00:00
|
|
|
// depending on the item kind, the location can define something in
|
|
|
|
// the values namespace, the types namespace, or both
|
|
|
|
let kind = DefKind::for_syntax_kind(item.syntax().kind());
|
|
|
|
let def_id = kind.map(|k| {
|
|
|
|
let item_id = file_items.id_of_unchecked(item.syntax());
|
|
|
|
let def_loc = DefLoc {
|
|
|
|
kind: k,
|
2019-01-23 20:14:13 +00:00
|
|
|
module,
|
2019-01-23 16:49:11 +00:00
|
|
|
source_item_id: SourceItemId {
|
|
|
|
file_id,
|
|
|
|
item_id: Some(item_id),
|
|
|
|
},
|
|
|
|
};
|
2019-01-23 20:14:13 +00:00
|
|
|
def_loc.id(db).into()
|
2019-01-23 16:49:11 +00:00
|
|
|
});
|
|
|
|
def_id
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DefKind {
|
|
|
|
fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
|
|
|
|
match kind {
|
2019-01-24 12:28:50 +00:00
|
|
|
SyntaxKind::FN_DEF => unreachable!(),
|
2019-01-24 15:56:38 +00:00
|
|
|
SyntaxKind::STRUCT_DEF => unreachable!(),
|
|
|
|
SyntaxKind::ENUM_DEF => unreachable!(),
|
2019-01-23 16:49:11 +00:00
|
|
|
SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Trait),
|
|
|
|
SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Type),
|
|
|
|
SyntaxKind::CONST_DEF => PerNs::values(DefKind::Const),
|
|
|
|
SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Static),
|
|
|
|
_ => PerNs::none(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|