From 77f90caf2deeb6a2d2c8196399fbba61bf0c461d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 29 Oct 2019 11:15:51 +0300 Subject: start ra_hir_def crate --- crates/ra_hir/Cargo.toml | 1 + crates/ra_hir/src/db.rs | 4 +- crates/ra_hir/src/expr/lower.rs | 11 ++- crates/ra_hir/src/from_source.rs | 4 +- crates/ra_hir/src/ids.rs | 2 +- crates/ra_hir/src/impl_block.rs | 4 +- crates/ra_hir/src/nameres/collector.rs | 13 ++-- crates/ra_hir/src/source_id.rs | 132 ++++++--------------------------- crates/ra_hir_def/Cargo.toml | 10 +++ crates/ra_hir_def/src/ast_id_map.rs | 114 ++++++++++++++++++++++++++++ crates/ra_hir_def/src/lib.rs | 7 ++ 11 files changed, 171 insertions(+), 131 deletions(-) create mode 100644 crates/ra_hir_def/Cargo.toml create mode 100644 crates/ra_hir_def/src/ast_id_map.rs create mode 100644 crates/ra_hir_def/src/lib.rs (limited to 'crates') diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index f05ec0b8a..82720da9e 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml @@ -19,6 +19,7 @@ ra_cfg = { path = "../ra_cfg" } ra_db = { path = "../ra_db" } mbe = { path = "../ra_mbe", package = "ra_mbe" } tt = { path = "../ra_tt", package = "ra_tt" } +hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } test_utils = { path = "../test_utils" } ra_prof = { path = "../ra_prof" } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 489a3b19c..7abbf8dca 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -59,11 +59,11 @@ pub trait InternDatabase: SourceDatabase { /// incremental. #[salsa::query_group(AstDatabaseStorage)] pub trait AstDatabase: InternDatabase { - #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)] + #[salsa::invoke(crate::source_id::ast_id_map_query)] fn ast_id_map(&self, file_id: HirFileId) -> Arc; #[salsa::transparent] - #[salsa::invoke(crate::source_id::AstIdMap::file_item_query)] + #[salsa::invoke(crate::source_id::file_item_query)] fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode; #[salsa::transparent] diff --git a/crates/ra_hir/src/expr/lower.rs b/crates/ra_hir/src/expr/lower.rs index 50ea429ea..24733b3de 100644 --- a/crates/ra_hir/src/expr/lower.rs +++ b/crates/ra_hir/src/expr/lower.rs @@ -16,7 +16,7 @@ use crate::{ path::GenericArgs, ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy}, type_ref::TypeRef, - DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver, + AstId, DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver, Source, }; @@ -458,11 +458,10 @@ where ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::Expr::MacroCall(e) => { - let ast_id = self - .db - .ast_id_map(self.current_file_id) - .ast_id(&e) - .with_file_id(self.current_file_id); + let ast_id = AstId::new( + self.current_file_id, + self.db.ast_id_map(self.current_file_id).ast_id(&e), + ); if let Some(path) = e.path().and_then(|path| self.parse_path(path)) { if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) { diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index f80d8eb5f..7954c04b2 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -11,7 +11,7 @@ use crate::{ db::{AstDatabase, DefDatabase, HirDatabase}, ids::{AstItemDef, LocationCtx}, name::AsName, - Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, + AstId, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, }; @@ -183,7 +183,7 @@ impl Module { ModuleSource::Module(ref module) => { assert!(!module.has_semi()); let ast_id_map = db.ast_id_map(src.file_id); - let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id); + let item_id = AstId::new(src.file_id, ast_id_map.ast_id(module)); Some(item_id) } ModuleSource::SourceFile(_) => None, diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 518ea32e9..f141206c6 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -264,7 +264,7 @@ pub(crate) trait AstItemDef: salsa::InternKey + Clone { Self::from_ast_id(ctx, item_id) } fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId) -> Self { - let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; + let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) }; Self::intern(ctx.db, loc) } fn source(self, db: &impl AstDatabase) -> Source { diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 33ef87563..9c739f3f1 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -20,7 +20,7 @@ use crate::{ resolve::Resolver, ty::Ty, type_ref::TypeRef, - AssocItem, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef, + AssocItem, AstId, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef, TypeAlias, }; @@ -256,7 +256,7 @@ impl ModuleImplBlocks { } //FIXME: we should really cut down on the boilerplate required to process a macro - let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); + let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(¯o_call)); if let Some(path) = macro_call .path() .and_then(|path| Path::from_src(Source { ast: path, file_id }, db)) diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index b5fe16bfa..4f363df36 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -567,7 +567,7 @@ where // inline module, just recurse raw::ModuleData::Definition { name, items, ast_id } => { let module_id = - self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); + self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None); ModCollector { def_collector: &mut *self.def_collector, @@ -583,7 +583,7 @@ where } // out of line module, resolve, parse and recurse raw::ModuleData::Declaration { name, ast_id } => { - let ast_id = ast_id.with_file_id(self.file_id); + let ast_id = AstId::new(self.file_id, *ast_id); match self.mod_dir.resolve_declaration( self.def_collector.db, self.file_id, @@ -671,21 +671,18 @@ where } fn collect_macro(&mut self, mac: &raw::MacroData) { + let ast_id = AstId::new(self.file_id, mac.ast_id); + // Case 1: macro rules, define a macro in crate-global mutable scope if is_macro_rules(&mac.path) { if let Some(name) = &mac.name { - let macro_id = MacroDefId { - ast_id: mac.ast_id.with_file_id(self.file_id), - krate: self.def_collector.def_map.krate, - }; + let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate }; let macro_ = MacroDef { id: macro_id }; self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); } return; } - let ast_id = mac.ast_id.with_file_id(self.file_id); - // Case 2: try to resolve in legacy scope and expand macro_rules, triggering // recursive item collection. if let Some(macro_def) = mac.path.as_ident().and_then(|name| { diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index a4dd99598..260b79661 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -2,18 +2,18 @@ use std::{ hash::{Hash, Hasher}, - marker::PhantomData, sync::Arc, }; -use ra_arena::{impl_arena_id, Arena, RawId}; -use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; +pub use hir_def::ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId}; +use ra_syntax::{AstNode, SyntaxNode}; use crate::{db::AstDatabase, HirFileId}; /// `AstId` points to an AST node in any file. /// /// It is stable across reparses, and can be used as salsa key/value. +// FIXME: isn't this just a `Source>` ? #[derive(Debug)] pub(crate) struct AstId { file_id: HirFileId, @@ -40,122 +40,34 @@ impl Hash for AstId { } impl AstId { + pub fn new(file_id: HirFileId, file_ast_id: FileAstId) -> AstId { + AstId { file_id, file_ast_id } + } + pub(crate) fn file_id(&self) -> HirFileId { self.file_id } pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N { - let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); + let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into()); N::cast(syntax_node).unwrap() } } -/// `AstId` points to an AST node in a specific file. -#[derive(Debug)] -pub(crate) struct FileAstId { - raw: ErasedFileAstId, - _ty: PhantomData N>, -} - -impl Clone for FileAstId { - fn clone(&self) -> FileAstId { - *self - } -} -impl Copy for FileAstId {} - -impl PartialEq for FileAstId { - fn eq(&self, other: &Self) -> bool { - self.raw == other.raw - } -} -impl Eq for FileAstId {} -impl Hash for FileAstId { - fn hash(&self, hasher: &mut H) { - self.raw.hash(hasher); - } -} - -impl FileAstId { - pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId { - AstId { file_id, file_ast_id: self } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ErasedFileAstId(RawId); -impl_arena_id!(ErasedFileAstId); - -/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. -#[derive(Debug, PartialEq, Eq, Default)] -pub struct AstIdMap { - arena: Arena, +pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc { + let map = if let Some(node) = db.parse_or_expand(file_id) { + AstIdMap::from_source(&node) + } else { + AstIdMap::default() + }; + Arc::new(map) } -impl AstIdMap { - pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc { - let map = if let Some(node) = db.parse_or_expand(file_id) { - AstIdMap::from_source(&node) - } else { - AstIdMap::default() - }; - Arc::new(map) - } - - pub(crate) fn file_item_query( - db: &impl AstDatabase, - file_id: HirFileId, - ast_id: ErasedFileAstId, - ) -> SyntaxNode { - let node = db.parse_or_expand(file_id).unwrap(); - db.ast_id_map(file_id).arena[ast_id].to_node(&node) - } - - pub(crate) fn ast_id(&self, item: &N) -> FileAstId { - let ptr = SyntaxNodePtr::new(item.syntax()); - let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { - Some((it, _)) => it, - None => panic!( - "Can't find {:?} in AstIdMap:\n{:?}", - item.syntax(), - self.arena.iter().map(|(_id, i)| i).collect::>(), - ), - }; - - FileAstId { raw, _ty: PhantomData } - } - - fn from_source(node: &SyntaxNode) -> AstIdMap { - assert!(node.parent().is_none()); - let mut res = AstIdMap { arena: Arena::default() }; - // By walking the tree in bread-first order we make sure that parents - // get lower ids then children. That is, adding a new child does not - // change parent's id. This means that, say, adding a new function to a - // trait does not change ids of top-level items, which helps caching. - bfs(node, |it| { - if let Some(module_item) = ast::ModuleItem::cast(it.clone()) { - res.alloc(module_item.syntax()); - } else if let Some(macro_call) = ast::MacroCall::cast(it) { - res.alloc(macro_call.syntax()); - } - }); - res - } - - fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { - self.arena.alloc(SyntaxNodePtr::new(item)) - } -} - -/// 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.clone()]; - 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); - } +pub(crate) fn file_item_query( + db: &impl AstDatabase, + file_id: HirFileId, + ast_id: ErasedFileAstId, +) -> SyntaxNode { + let node = db.parse_or_expand(file_id).unwrap(); + db.ast_id_map(file_id)[ast_id].to_node(&node) } diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml new file mode 100644 index 000000000..7c57d56bd --- /dev/null +++ b/crates/ra_hir_def/Cargo.toml @@ -0,0 +1,10 @@ +[package] +edition = "2018" +name = "ra_hir_def" +version = "0.1.0" +authors = ["rust-analyzer developers"] + +[dependencies] +ra_arena = { path = "../ra_arena" } +ra_db = { path = "../ra_db" } +ra_syntax = { path = "../ra_syntax" } diff --git a/crates/ra_hir_def/src/ast_id_map.rs b/crates/ra_hir_def/src/ast_id_map.rs new file mode 100644 index 000000000..c3b389102 --- /dev/null +++ b/crates/ra_hir_def/src/ast_id_map.rs @@ -0,0 +1,114 @@ +//! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items +//! and macro calls. +//! +//! Specifically, it enumerates all items in a file and uses position of a an +//! item as an ID. That way, id's don't change unless the set of items itself +//! changes. + +use std::{ + hash::{Hash, Hasher}, + marker::PhantomData, + ops, +}; + +use ra_arena::{impl_arena_id, Arena, RawId}; +use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; + +/// `AstId` points to an AST node in a specific file. +#[derive(Debug)] +pub struct FileAstId { + raw: ErasedFileAstId, + _ty: PhantomData N>, +} + +impl Clone for FileAstId { + fn clone(&self) -> FileAstId { + *self + } +} +impl Copy for FileAstId {} + +impl PartialEq for FileAstId { + fn eq(&self, other: &Self) -> bool { + self.raw == other.raw + } +} +impl Eq for FileAstId {} +impl Hash for FileAstId { + fn hash(&self, hasher: &mut H) { + self.raw.hash(hasher); + } +} + +impl From> for ErasedFileAstId { + fn from(id: FileAstId) -> Self { + id.raw + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ErasedFileAstId(RawId); +impl_arena_id!(ErasedFileAstId); + +/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. +#[derive(Debug, PartialEq, Eq, Default)] +pub struct AstIdMap { + arena: Arena, +} + +impl AstIdMap { + pub fn from_source(node: &SyntaxNode) -> AstIdMap { + assert!(node.parent().is_none()); + let mut res = AstIdMap { arena: Arena::default() }; + // By walking the tree in bread-first order we make sure that parents + // get lower ids then children. That is, adding a new child does not + // change parent's id. This means that, say, adding a new function to a + // trait does not change ids of top-level items, which helps caching. + bfs(node, |it| { + if let Some(module_item) = ast::ModuleItem::cast(it.clone()) { + res.alloc(module_item.syntax()); + } else if let Some(macro_call) = ast::MacroCall::cast(it) { + res.alloc(macro_call.syntax()); + } + }); + res + } + + pub fn ast_id(&self, item: &N) -> FileAstId { + let ptr = SyntaxNodePtr::new(item.syntax()); + let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { + Some((it, _)) => it, + None => panic!( + "Can't find {:?} in AstIdMap:\n{:?}", + item.syntax(), + self.arena.iter().map(|(_id, i)| i).collect::>(), + ), + }; + + FileAstId { raw, _ty: PhantomData } + } + + fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { + self.arena.alloc(SyntaxNodePtr::new(item)) + } +} + +impl ops::Index for AstIdMap { + type Output = SyntaxNodePtr; + fn index(&self, index: ErasedFileAstId) -> &SyntaxNodePtr { + &self.arena[index] + } +} + +/// 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.clone()]; + 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); + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs new file mode 100644 index 000000000..4d4d2cb19 --- /dev/null +++ b/crates/ra_hir_def/src/lib.rs @@ -0,0 +1,7 @@ +//! `ra_hir_def` contains initial "phases" of the compiler. Roughly, everything +//! before types. +//! +//! Note that we are in the process of moving parts of `ra_hir` into +//! `ra_hir_def`, so this crates doesn't contain a lot at the moment. + +pub mod ast_id_map; -- cgit v1.2.3