From 42cc77703b041436b2507816cb394ad6fd3d07e0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 2 Jan 2019 00:30:00 +0300 Subject: move more stuff to ids --- crates/ra_hir/src/ids.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir/src/ids.rs') diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 3eba35a24..cd32033f3 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -1,6 +1,10 @@ -use crate::{FileId, MacroCallId, HirDatabase}; +use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; +use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; -use ra_syntax::SourceFileNode; +use crate::{ + MacroCallId, HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, + arena::{Arena, Id}, +}; /// hir makes a heavy use of ids: integer (u32) handlers to various things. You /// can think of id as a pointer (but without a lifetime) or a file descriptor @@ -72,3 +76,170 @@ impl From for HirFileId { HirFileId(HirFileIdRepr::Macro(macro_call_id)) } } + +/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) +/// in a specific module. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DefId(u32); +ra_db::impl_numeric_id!(DefId); + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct DefLoc { + pub(crate) kind: DefKind, + pub(crate) source_root_id: SourceRootId, + pub(crate) module_id: ModuleId, + pub(crate) source_item_id: SourceItemId, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(crate) enum DefKind { + Module, + Function, + Struct, + Enum, + Item, + + StructCtor, +} + +impl DefId { + pub(crate) fn loc(self, db: &impl AsRef>) -> DefLoc { + db.as_ref().id2loc(self) + } + + pub fn resolve(self, db: &impl HirDatabase) -> Cancelable { + let loc = self.loc(db); + let res = match loc.kind { + DefKind::Module => { + let module = Module::new(db, loc.source_root_id, loc.module_id)?; + Def::Module(module) + } + DefKind::Function => { + let function = Function::new(self); + Def::Function(function) + } + DefKind::Struct => { + let struct_def = Struct::new(self); + Def::Struct(struct_def) + } + DefKind::Enum => { + let enum_def = Enum::new(self); + Def::Enum(enum_def) + } + DefKind::StructCtor => Def::Item, + DefKind::Item => Def::Item, + }; + Ok(res) + } + + /// For a module, returns that module; for any other def, returns the containing module. + pub fn module(self, db: &impl HirDatabase) -> Cancelable { + let loc = self.loc(db); + Module::new(db, loc.source_root_id, loc.module_id) + } +} + +impl DefLoc { + pub(crate) fn id(&self, db: &impl AsRef>) -> DefId { + db.as_ref().loc2id(&self) + } +} + +impl DefKind { + pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs { + match kind { + SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), + SyntaxKind::MODULE => PerNs::types(DefKind::Module), + SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), + SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), + // These define items, but don't have their own DefKinds yet: + SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), + SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), + SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), + SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), + _ => PerNs::none(), + } + } +} + +/// Identifier of item within a specific file. This is stable over reparses, so +/// it's OK to use it as a salsa key/value. +pub(crate) type SourceFileItemId = Id; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SourceItemId { + pub(crate) file_id: HirFileId, + /// None for the whole file. + pub(crate) item_id: Option, +} + +/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. +#[derive(Debug, PartialEq, Eq)] +pub struct SourceFileItems { + file_id: HirFileId, + arena: Arena, +} + +impl SourceFileItems { + pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems { + let mut res = SourceFileItems { + file_id, + arena: Arena::default(), + }; + res.init(source_file); + res + } + + fn init(&mut self, source_file: SourceFile) { + source_file.syntax().descendants().for_each(|it| { + if let Some(module_item) = ast::ModuleItem::cast(it) { + self.alloc(module_item.syntax().owned()); + } else if let Some(macro_call) = ast::MacroCall::cast(it) { + self.alloc(macro_call.syntax().owned()); + } + }); + } + + fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { + self.arena.alloc(item) + } + pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId { + assert_eq!( + self.file_id, file_id, + "SourceFileItems: wrong file, expected {:?}, got {:?}", + self.file_id, file_id + ); + self.id_of_unchecked(item) + } + pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId { + if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) { + return id; + } + // This should not happen. Let's try to give a sensible diagnostics. + if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { + // FIXME(#288): whyyy are we getting here? + log::error!( + "unequal syntax nodes with the same range:\n{:?}\n{:?}", + item, + i + ); + return id; + } + panic!( + "Can't find {:?} in SourceFileItems:\n{:?}", + item, + self.arena.iter().map(|(_id, i)| i).collect::>(), + ); + } + pub fn id_of_source_file(&self) -> SourceFileItemId { + let (id, _syntax) = self.arena.iter().next().unwrap(); + id + } +} + +impl std::ops::Index for SourceFileItems { + type Output = SyntaxNode; + fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { + &self.arena[idx] + } +} -- cgit v1.2.3