rust/crates/ra_hir/src/ids.rs

391 lines
12 KiB
Rust
Raw Normal View History

2019-01-24 22:02:18 +01:00
use std::{
marker::PhantomData,
2019-01-24 23:05:50 +01:00
hash::{Hash, Hasher},
2019-01-24 22:02:18 +01:00
};
2019-01-24 13:28:50 +01:00
use ra_db::{LocationIntener, FileId};
2019-01-23 17:49:11 +01:00
use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, ast};
2019-01-24 22:02:18 +01:00
use ra_arena::{Arena, RawId, ArenaId, impl_arena_id};
2019-01-01 21:21:16 +01:00
2019-01-08 13:57:45 +01:00
use crate::{
2019-01-24 23:41:36 +01:00
HirDatabase,
2019-01-24 23:31:32 +01:00
Module,
2019-01-08 13:57:45 +01:00
};
2019-01-06 13:16:21 +01:00
2019-01-24 10:41:08 +01:00
#[derive(Debug, Default)]
pub struct HirInterner {
macros: LocationIntener<MacroCallLoc, MacroCallId>,
2019-01-24 22:26:54 +01:00
fns: LocationIntener<ItemLoc<ast::FnDef>, FunctionId>,
structs: LocationIntener<ItemLoc<ast::StructDef>, StructId>,
enums: LocationIntener<ItemLoc<ast::EnumDef>, EnumId>,
2019-01-24 22:50:08 +01:00
consts: LocationIntener<ItemLoc<ast::ConstDef>, ConstId>,
statics: LocationIntener<ItemLoc<ast::StaticDef>, StaticId>,
2019-01-24 23:31:32 +01:00
traits: LocationIntener<ItemLoc<ast::TraitDef>, TraitId>,
types: LocationIntener<ItemLoc<ast::TypeDef>, TypeId>,
2019-01-24 10:41:08 +01:00
}
impl HirInterner {
pub fn len(&self) -> usize {
2019-01-24 23:41:36 +01:00
self.macros.len()
+ self.fns.len()
+ self.structs.len()
+ self.enums.len()
+ self.consts.len()
+ self.statics.len()
+ self.traits.len()
+ self.types.len()
2019-01-24 10:41:08 +01:00
}
}
/// hir makes heavy use of ids: integer (u32) handlers to various things. You
2019-01-01 20:47:10 +01:00
/// can think of id as a pointer (but without a lifetime) or a file descriptor
/// (but for hir objects).
///
/// This module defines a bunch of ids we are using. The most important ones are
/// probably `HirFileId` and `DefId`.
/// Input to the analyzer is a set of files, where each file is indentified by
2019-01-01 20:47:10 +01:00
/// `FileId` and contains source code. However, another source of source code in
/// Rust are macros: each macro can be thought of as producing a "temporary
/// file". To assign an id to such a file, we use the id of the macro call that
2019-01-01 20:47:10 +01:00
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
/// written by user), or a `MacroCallId` (source code produced by macro).
///
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
/// the call plus the offset of the macro call in the file. Note that this is a
/// recursive definition! However, the size_of of `HirFileId` is finite
2019-01-01 20:47:10 +01:00
/// (because everything bottoms out at the real `FileId`) and small
/// (`MacroCallId` uses the location interner).
2019-01-01 20:47:10 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2019-01-01 21:21:16 +01:00
pub struct HirFileId(HirFileIdRepr);
impl HirFileId {
2019-01-02 14:00:01 +01:00
/// For macro-expansion files, returns the file original source file the
/// expansionoriginated from.
pub fn original_file(self, db: &impl HirDatabase) -> FileId {
2019-01-01 21:21:16 +01:00
match self.0 {
HirFileIdRepr::File(file_id) => file_id,
HirFileIdRepr::Macro(macro_call_id) => {
let loc = macro_call_id.loc(db);
2019-01-02 14:00:35 +01:00
loc.source_item_id.file_id.original_file(db)
2019-01-01 21:21:16 +01:00
}
}
}
pub(crate) fn as_original_file(self) -> FileId {
match self.0 {
HirFileIdRepr::File(file_id) => file_id,
HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
}
}
2019-01-02 14:00:01 +01:00
2019-01-03 19:28:35 +01:00
pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> {
match self.0 {
HirFileIdRepr::Macro(it) => Some(it),
_ => None,
}
}
2019-01-26 09:51:36 +01:00
pub(crate) fn hir_parse(db: &impl HirDatabase, file_id: HirFileId) -> TreeArc<SourceFile> {
2019-01-01 21:21:16 +01:00
match file_id.0 {
2019-01-26 09:51:36 +01:00
HirFileIdRepr::File(file_id) => db.parse(file_id),
2019-01-01 21:21:16 +01:00
HirFileIdRepr::Macro(m) => {
if let Some(exp) = db.expand_macro_invocation(m) {
return exp.file();
}
// returning an empty string looks fishy...
2019-01-08 09:28:42 +01:00
SourceFile::parse("")
2019-01-01 21:21:16 +01:00
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HirFileIdRepr {
2019-01-01 20:47:10 +01:00
File(FileId),
Macro(MacroCallId),
}
2019-01-01 21:21:16 +01:00
impl From<FileId> for HirFileId {
fn from(file_id: FileId) -> HirFileId {
HirFileId(HirFileIdRepr::File(file_id))
}
}
impl From<MacroCallId> for HirFileId {
fn from(macro_call_id: MacroCallId) -> HirFileId {
HirFileId(HirFileIdRepr::Macro(macro_call_id))
2019-01-01 20:47:10 +01:00
}
}
2019-01-01 22:30:00 +01:00
2019-01-01 22:37:36 +01:00
/// `MacroCallId` identifies a particular macro invocation, like
/// `println!("Hello, {}", world)`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2019-01-08 13:53:32 +01:00
pub struct MacroCallId(RawId);
impl_arena_id!(MacroCallId);
2019-01-01 22:37:36 +01:00
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2019-01-01 22:37:36 +01:00
pub struct MacroCallLoc {
pub(crate) module: Module,
2019-01-01 22:37:36 +01:00
pub(crate) source_item_id: SourceItemId,
}
impl MacroCallId {
2019-01-24 10:41:08 +01:00
pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> MacroCallLoc {
db.as_ref().macros.id2loc(self)
2019-01-01 22:37:36 +01:00
}
}
impl MacroCallLoc {
#[allow(unused)]
2019-01-24 10:41:08 +01:00
pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId {
db.as_ref().macros.loc2id(&self)
2019-01-01 22:37:36 +01:00
}
}
2019-01-24 23:05:50 +01:00
#[derive(Debug)]
2019-01-24 13:28:50 +01:00
pub struct ItemLoc<N: AstNode> {
pub(crate) module: Module,
raw: SourceItemId,
_ty: PhantomData<N>,
}
2019-01-24 23:05:50 +01:00
impl<N: AstNode> PartialEq for ItemLoc<N> {
fn eq(&self, other: &Self) -> bool {
self.module == other.module && self.raw == other.raw
}
}
impl<N: AstNode> Eq for ItemLoc<N> {}
impl<N: AstNode> Hash for ItemLoc<N> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.module.hash(hasher);
self.raw.hash(hasher);
}
}
2019-01-24 22:26:54 +01:00
impl<N: AstNode> Clone for ItemLoc<N> {
fn clone(&self) -> ItemLoc<N> {
2019-01-24 13:28:50 +01:00
ItemLoc {
2019-01-24 22:26:54 +01:00
module: self.module,
raw: self.raw,
2019-01-24 13:28:50 +01:00
_ty: PhantomData,
}
}
2019-01-24 22:26:54 +01:00
}
2019-01-24 13:28:50 +01:00
2019-01-24 22:26:54 +01:00
#[derive(Clone, Copy)]
pub(crate) struct LocationCtx<DB> {
db: DB,
module: Module,
file_id: HirFileId,
2019-01-24 13:28:50 +01:00
}
2019-01-24 22:26:54 +01:00
impl<'a, DB: HirDatabase> LocationCtx<&'a DB> {
pub(crate) fn new(db: &'a DB, module: Module, file_id: HirFileId) -> LocationCtx<&'a DB> {
LocationCtx {
db,
module,
file_id,
2019-01-24 13:28:50 +01:00
}
}
2019-01-24 22:26:54 +01:00
pub(crate) fn to_def<N, DEF>(self, ast: &N) -> DEF
where
2019-01-24 23:05:50 +01:00
N: AstNode,
2019-01-24 22:26:54 +01:00
DEF: AstItemDef<N>,
{
DEF::from_ast(self, ast)
}
2019-01-24 13:28:50 +01:00
}
2019-01-24 23:05:50 +01:00
pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone {
2019-01-24 22:02:18 +01:00
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>;
2019-01-24 22:26:54 +01:00
fn from_ast(ctx: LocationCtx<&impl HirDatabase>, ast: &N) -> Self {
let items = ctx.db.file_items(ctx.file_id);
let raw = SourceItemId {
file_id: ctx.file_id,
item_id: Some(items.id_of(ctx.file_id, ast.syntax())),
};
let loc = ItemLoc {
module: ctx.module,
raw,
_ty: PhantomData,
};
Self::interner(ctx.db.as_ref()).loc2id(&loc)
}
2019-01-24 22:02:18 +01:00
fn source(self, db: &impl HirDatabase) -> (HirFileId, TreeArc<N>) {
let int = Self::interner(db.as_ref());
let loc = int.id2loc(self);
2019-01-24 22:26:54 +01:00
let syntax = db.file_item(loc.raw);
let ast = N::cast(&syntax)
.unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw))
.to_owned();
(loc.raw.file_id, ast)
2019-01-24 22:02:18 +01:00
}
fn module(self, db: &impl HirDatabase) -> Module {
let int = Self::interner(db.as_ref());
let loc = int.id2loc(self);
loc.module
}
}
2019-01-24 11:34:41 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FunctionId(RawId);
impl_arena_id!(FunctionId);
2019-01-24 22:02:18 +01:00
impl AstItemDef<ast::FnDef> for FunctionId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::FnDef>, Self> {
&interner.fns
2019-01-24 11:34:41 +01:00
}
}
2019-01-24 14:18:20 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructId(RawId);
impl_arena_id!(StructId);
2019-01-24 22:02:18 +01:00
impl AstItemDef<ast::StructDef> for StructId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::StructDef>, Self> {
&interner.structs
2019-01-24 14:18:20 +01:00
}
}
2019-01-24 15:56:00 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumId(RawId);
impl_arena_id!(EnumId);
2019-01-24 22:02:18 +01:00
impl AstItemDef<ast::EnumDef> for EnumId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::EnumDef>, Self> {
&interner.enums
2019-01-24 15:56:00 +01:00
}
}
2019-01-24 22:50:08 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(RawId);
impl_arena_id!(ConstId);
impl AstItemDef<ast::ConstDef> for ConstId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::ConstDef>, Self> {
&interner.consts
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StaticId(RawId);
impl_arena_id!(StaticId);
impl AstItemDef<ast::StaticDef> for StaticId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::StaticDef>, Self> {
&interner.statics
}
}
2019-01-24 23:31:32 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TraitId(RawId);
impl_arena_id!(TraitId);
impl AstItemDef<ast::TraitDef> for TraitId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::TraitDef>, Self> {
&interner.traits
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeId(RawId);
impl_arena_id!(TypeId);
impl AstItemDef<ast::TypeDef> for TypeId {
fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<ast::TypeDef>, Self> {
&interner.types
}
}
2019-01-01 22:30:00 +01:00
/// Identifier of item within a specific file. This is stable over reparses, so
/// it's OK to use it as a salsa key/value.
2019-01-04 14:15:50 +01:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SourceFileItemId(RawId);
impl_arena_id!(SourceFileItemId);
2019-01-01 22:30:00 +01:00
#[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<SourceFileItemId>,
}
/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back.
2019-01-01 22:30:00 +01:00
#[derive(Debug, PartialEq, Eq)]
pub struct SourceFileItems {
file_id: HirFileId,
arena: Arena<SourceFileItemId, TreeArc<SyntaxNode>>,
2019-01-01 22:30:00 +01:00
}
impl SourceFileItems {
2019-01-08 09:28:42 +01:00
pub(crate) fn new(file_id: HirFileId, source_file: &SourceFile) -> SourceFileItems {
2019-01-01 22:30:00 +01:00
let mut res = SourceFileItems {
file_id,
arena: Arena::default(),
};
res.init(source_file);
res
}
2019-01-08 09:28:42 +01:00
fn init(&mut self, source_file: &SourceFile) {
// By walking the tree in bread-first order we make sure that parents
// get lower ids then children. That is, addding a new child does not
// change parent's id. This means that, say, adding a new function to a
// trait does not chage ids of top-level items, which helps caching.
bfs(source_file.syntax(), |it| {
2019-01-25 09:35:38 +01:00
if let Some(module_item) = ast::ModuleItem::cast(it) {
2019-01-08 09:28:42 +01:00
self.alloc(module_item.syntax().to_owned());
2019-01-01 22:30:00 +01:00
} else if let Some(macro_call) = ast::MacroCall::cast(it) {
2019-01-08 09:28:42 +01:00
self.alloc(macro_call.syntax().to_owned());
2019-01-01 22:30:00 +01:00
}
})
2019-01-01 22:30:00 +01:00
}
fn alloc(&mut self, item: TreeArc<SyntaxNode>) -> SourceFileItemId {
2019-01-01 22:30:00 +01:00
self.arena.alloc(item)
}
2019-01-08 09:28:42 +01:00
pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId {
2019-01-01 22:30:00 +01:00
assert_eq!(
self.file_id, file_id,
"SourceFileItems: wrong file, expected {:?}, got {:?}",
self.file_id, file_id
);
self.id_of_unchecked(item)
}
2019-01-08 09:28:42 +01:00
pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId {
if let Some((id, _)) = self.arena.iter().find(|(_id, i)| *i == item) {
2019-01-01 22:30:00 +01:00
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::<Vec<_>>(),
);
}
}
impl std::ops::Index<SourceFileItemId> for SourceFileItems {
type Output = SyntaxNode;
fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
&self.arena[idx]
}
}
/// Walks the subtree in bfs order, calling `f` for each node.
fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) {
let mut curr_layer = vec![node];
let mut next_layer = vec![];
while !curr_layer.is_empty() {
curr_layer.drain(..).for_each(|node| {
next_layer.extend(node.children());
f(node);
});
std::mem::swap(&mut curr_layer, &mut next_layer);
}
}