aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-10-29 08:20:02 +0000
committerGitHub <[email protected]>2019-10-29 08:20:02 +0000
commit4f22d2f3b0852f32c0ba5e4545ec8cc2d986cfcc (patch)
tree09667ecbdcc8b84916c509d3a3d09ab56b5d4aed
parent120000609ab0a0e6a946404d0477f5a0a7107800 (diff)
parent77f90caf2deeb6a2d2c8196399fbba61bf0c461d (diff)
Merge #2112
2112: start ra_hir_def crate r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--Cargo.lock10
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/db.rs4
-rw-r--r--crates/ra_hir/src/expr/lower.rs11
-rw-r--r--crates/ra_hir/src/from_source.rs4
-rw-r--r--crates/ra_hir/src/ids.rs2
-rw-r--r--crates/ra_hir/src/impl_block.rs4
-rw-r--r--crates/ra_hir/src/nameres/collector.rs13
-rw-r--r--crates/ra_hir/src/source_id.rs132
-rw-r--r--crates/ra_hir_def/Cargo.toml10
-rw-r--r--crates/ra_hir_def/src/ast_id_map.rs114
-rw-r--r--crates/ra_hir_def/src/lib.rs7
12 files changed, 181 insertions, 131 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0206f16bc..260be7289 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -991,6 +991,7 @@ dependencies = [
991 "ra_arena 0.1.0", 991 "ra_arena 0.1.0",
992 "ra_cfg 0.1.0", 992 "ra_cfg 0.1.0",
993 "ra_db 0.1.0", 993 "ra_db 0.1.0",
994 "ra_hir_def 0.1.0",
994 "ra_mbe 0.1.0", 995 "ra_mbe 0.1.0",
995 "ra_prof 0.1.0", 996 "ra_prof 0.1.0",
996 "ra_syntax 0.1.0", 997 "ra_syntax 0.1.0",
@@ -1001,6 +1002,15 @@ dependencies = [
1001] 1002]
1002 1003
1003[[package]] 1004[[package]]
1005name = "ra_hir_def"
1006version = "0.1.0"
1007dependencies = [
1008 "ra_arena 0.1.0",
1009 "ra_db 0.1.0",
1010 "ra_syntax 0.1.0",
1011]
1012
1013[[package]]
1004name = "ra_ide_api" 1014name = "ra_ide_api"
1005version = "0.1.0" 1015version = "0.1.0"
1006dependencies = [ 1016dependencies = [
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" }
19ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
20mbe = { path = "../ra_mbe", package = "ra_mbe" } 20mbe = { path = "../ra_mbe", package = "ra_mbe" }
21tt = { path = "../ra_tt", package = "ra_tt" } 21tt = { path = "../ra_tt", package = "ra_tt" }
22hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
22test_utils = { path = "../test_utils" } 23test_utils = { path = "../test_utils" }
23ra_prof = { path = "../ra_prof" } 24ra_prof = { path = "../ra_prof" }
24 25
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 {
59/// incremental. 59/// incremental.
60#[salsa::query_group(AstDatabaseStorage)] 60#[salsa::query_group(AstDatabaseStorage)]
61pub trait AstDatabase: InternDatabase { 61pub trait AstDatabase: InternDatabase {
62 #[salsa::invoke(crate::source_id::AstIdMap::ast_id_map_query)] 62 #[salsa::invoke(crate::source_id::ast_id_map_query)]
63 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; 63 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
64 64
65 #[salsa::transparent] 65 #[salsa::transparent]
66 #[salsa::invoke(crate::source_id::AstIdMap::file_item_query)] 66 #[salsa::invoke(crate::source_id::file_item_query)]
67 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode; 67 fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode;
68 68
69 #[salsa::transparent] 69 #[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::{
16 path::GenericArgs, 16 path::GenericArgs,
17 ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy}, 17 ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
18 type_ref::TypeRef, 18 type_ref::TypeRef,
19 DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver, 19 AstId, DefWithBody, Either, HirFileId, MacroCallLoc, MacroFileKind, Mutability, Path, Resolver,
20 Source, 20 Source,
21}; 21};
22 22
@@ -458,11 +458,10 @@ where
458 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), 458 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
459 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), 459 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
460 ast::Expr::MacroCall(e) => { 460 ast::Expr::MacroCall(e) => {
461 let ast_id = self 461 let ast_id = AstId::new(
462 .db 462 self.current_file_id,
463 .ast_id_map(self.current_file_id) 463 self.db.ast_id_map(self.current_file_id).ast_id(&e),
464 .ast_id(&e) 464 );
465 .with_file_id(self.current_file_id);
466 465
467 if let Some(path) = e.path().and_then(|path| self.parse_path(path)) { 466 if let Some(path) = e.path().and_then(|path| self.parse_path(path)) {
468 if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) { 467 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::{
11 db::{AstDatabase, DefDatabase, HirDatabase}, 11 db::{AstDatabase, DefDatabase, HirDatabase},
12 ids::{AstItemDef, LocationCtx}, 12 ids::{AstItemDef, LocationCtx},
13 name::AsName, 13 name::AsName,
14 Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, 14 AstId, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
15 ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, 15 ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
16}; 16};
17 17
@@ -183,7 +183,7 @@ impl Module {
183 ModuleSource::Module(ref module) => { 183 ModuleSource::Module(ref module) => {
184 assert!(!module.has_semi()); 184 assert!(!module.has_semi());
185 let ast_id_map = db.ast_id_map(src.file_id); 185 let ast_id_map = db.ast_id_map(src.file_id);
186 let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id); 186 let item_id = AstId::new(src.file_id, ast_id_map.ast_id(module));
187 Some(item_id) 187 Some(item_id)
188 } 188 }
189 ModuleSource::SourceFile(_) => None, 189 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<N: AstNode>: salsa::InternKey + Clone {
264 Self::from_ast_id(ctx, item_id) 264 Self::from_ast_id(ctx, item_id)
265 } 265 }
266 fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId<N>) -> Self { 266 fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId<N>) -> Self {
267 let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) }; 267 let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) };
268 Self::intern(ctx.db, loc) 268 Self::intern(ctx.db, loc)
269 } 269 }
270 fn source(self, db: &impl AstDatabase) -> Source<N> { 270 fn source(self, db: &impl AstDatabase) -> Source<N> {
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::{
20 resolve::Resolver, 20 resolve::Resolver,
21 ty::Ty, 21 ty::Ty,
22 type_ref::TypeRef, 22 type_ref::TypeRef,
23 AssocItem, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef, 23 AssocItem, AstId, Const, Function, HasSource, HirFileId, MacroFileKind, Path, Source, TraitRef,
24 TypeAlias, 24 TypeAlias,
25}; 25};
26 26
@@ -256,7 +256,7 @@ impl ModuleImplBlocks {
256 } 256 }
257 257
258 //FIXME: we should really cut down on the boilerplate required to process a macro 258 //FIXME: we should really cut down on the boilerplate required to process a macro
259 let ast_id = db.ast_id_map(file_id).ast_id(&macro_call).with_file_id(file_id); 259 let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(&macro_call));
260 if let Some(path) = macro_call 260 if let Some(path) = macro_call
261 .path() 261 .path()
262 .and_then(|path| Path::from_src(Source { ast: path, file_id }, db)) 262 .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
567 // inline module, just recurse 567 // inline module, just recurse
568 raw::ModuleData::Definition { name, items, ast_id } => { 568 raw::ModuleData::Definition { name, items, ast_id } => {
569 let module_id = 569 let module_id =
570 self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); 570 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None);
571 571
572 ModCollector { 572 ModCollector {
573 def_collector: &mut *self.def_collector, 573 def_collector: &mut *self.def_collector,
@@ -583,7 +583,7 @@ where
583 } 583 }
584 // out of line module, resolve, parse and recurse 584 // out of line module, resolve, parse and recurse
585 raw::ModuleData::Declaration { name, ast_id } => { 585 raw::ModuleData::Declaration { name, ast_id } => {
586 let ast_id = ast_id.with_file_id(self.file_id); 586 let ast_id = AstId::new(self.file_id, *ast_id);
587 match self.mod_dir.resolve_declaration( 587 match self.mod_dir.resolve_declaration(
588 self.def_collector.db, 588 self.def_collector.db,
589 self.file_id, 589 self.file_id,
@@ -671,21 +671,18 @@ where
671 } 671 }
672 672
673 fn collect_macro(&mut self, mac: &raw::MacroData) { 673 fn collect_macro(&mut self, mac: &raw::MacroData) {
674 let ast_id = AstId::new(self.file_id, mac.ast_id);
675
674 // Case 1: macro rules, define a macro in crate-global mutable scope 676 // Case 1: macro rules, define a macro in crate-global mutable scope
675 if is_macro_rules(&mac.path) { 677 if is_macro_rules(&mac.path) {
676 if let Some(name) = &mac.name { 678 if let Some(name) = &mac.name {
677 let macro_id = MacroDefId { 679 let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
678 ast_id: mac.ast_id.with_file_id(self.file_id),
679 krate: self.def_collector.def_map.krate,
680 };
681 let macro_ = MacroDef { id: macro_id }; 680 let macro_ = MacroDef { id: macro_id };
682 self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); 681 self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
683 } 682 }
684 return; 683 return;
685 } 684 }
686 685
687 let ast_id = mac.ast_id.with_file_id(self.file_id);
688
689 // Case 2: try to resolve in legacy scope and expand macro_rules, triggering 686 // Case 2: try to resolve in legacy scope and expand macro_rules, triggering
690 // recursive item collection. 687 // recursive item collection.
691 if let Some(macro_def) = mac.path.as_ident().and_then(|name| { 688 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 @@
2 2
3use std::{ 3use std::{
4 hash::{Hash, Hasher}, 4 hash::{Hash, Hasher},
5 marker::PhantomData,
6 sync::Arc, 5 sync::Arc,
7}; 6};
8 7
9use ra_arena::{impl_arena_id, Arena, RawId}; 8pub use hir_def::ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId};
10use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; 9use ra_syntax::{AstNode, SyntaxNode};
11 10
12use crate::{db::AstDatabase, HirFileId}; 11use crate::{db::AstDatabase, HirFileId};
13 12
14/// `AstId` points to an AST node in any file. 13/// `AstId` points to an AST node in any file.
15/// 14///
16/// It is stable across reparses, and can be used as salsa key/value. 15/// It is stable across reparses, and can be used as salsa key/value.
16// FIXME: isn't this just a `Source<FileAstId<N>>` ?
17#[derive(Debug)] 17#[derive(Debug)]
18pub(crate) struct AstId<N: AstNode> { 18pub(crate) struct AstId<N: AstNode> {
19 file_id: HirFileId, 19 file_id: HirFileId,
@@ -40,122 +40,34 @@ impl<N: AstNode> Hash for AstId<N> {
40} 40}
41 41
42impl<N: AstNode> AstId<N> { 42impl<N: AstNode> AstId<N> {
43 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
44 AstId { file_id, file_ast_id }
45 }
46
43 pub(crate) fn file_id(&self) -> HirFileId { 47 pub(crate) fn file_id(&self) -> HirFileId {
44 self.file_id 48 self.file_id
45 } 49 }
46 50
47 pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N { 51 pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N {
48 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); 52 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into());
49 N::cast(syntax_node).unwrap() 53 N::cast(syntax_node).unwrap()
50 } 54 }
51} 55}
52 56
53/// `AstId` points to an AST node in a specific file. 57pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
54#[derive(Debug)] 58 let map = if let Some(node) = db.parse_or_expand(file_id) {
55pub(crate) struct FileAstId<N: AstNode> { 59 AstIdMap::from_source(&node)
56 raw: ErasedFileAstId, 60 } else {
57 _ty: PhantomData<fn() -> N>, 61 AstIdMap::default()
58} 62 };
59 63 Arc::new(map)
60impl<N: AstNode> Clone for FileAstId<N> {
61 fn clone(&self) -> FileAstId<N> {
62 *self
63 }
64}
65impl<N: AstNode> Copy for FileAstId<N> {}
66
67impl<N: AstNode> PartialEq for FileAstId<N> {
68 fn eq(&self, other: &Self) -> bool {
69 self.raw == other.raw
70 }
71}
72impl<N: AstNode> Eq for FileAstId<N> {}
73impl<N: AstNode> Hash for FileAstId<N> {
74 fn hash<H: Hasher>(&self, hasher: &mut H) {
75 self.raw.hash(hasher);
76 }
77}
78
79impl<N: AstNode> FileAstId<N> {
80 pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> {
81 AstId { file_id, file_ast_id: self }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
86pub struct ErasedFileAstId(RawId);
87impl_arena_id!(ErasedFileAstId);
88
89/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
90#[derive(Debug, PartialEq, Eq, Default)]
91pub struct AstIdMap {
92 arena: Arena<ErasedFileAstId, SyntaxNodePtr>,
93} 64}
94 65
95impl AstIdMap { 66pub(crate) fn file_item_query(
96 pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 67 db: &impl AstDatabase,
97 let map = if let Some(node) = db.parse_or_expand(file_id) { 68 file_id: HirFileId,
98 AstIdMap::from_source(&node) 69 ast_id: ErasedFileAstId,
99 } else { 70) -> SyntaxNode {
100 AstIdMap::default() 71 let node = db.parse_or_expand(file_id).unwrap();
101 }; 72 db.ast_id_map(file_id)[ast_id].to_node(&node)
102 Arc::new(map)
103 }
104
105 pub(crate) fn file_item_query(
106 db: &impl AstDatabase,
107 file_id: HirFileId,
108 ast_id: ErasedFileAstId,
109 ) -> SyntaxNode {
110 let node = db.parse_or_expand(file_id).unwrap();
111 db.ast_id_map(file_id).arena[ast_id].to_node(&node)
112 }
113
114 pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
115 let ptr = SyntaxNodePtr::new(item.syntax());
116 let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) {
117 Some((it, _)) => it,
118 None => panic!(
119 "Can't find {:?} in AstIdMap:\n{:?}",
120 item.syntax(),
121 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
122 ),
123 };
124
125 FileAstId { raw, _ty: PhantomData }
126 }
127
128 fn from_source(node: &SyntaxNode) -> AstIdMap {
129 assert!(node.parent().is_none());
130 let mut res = AstIdMap { arena: Arena::default() };
131 // By walking the tree in bread-first order we make sure that parents
132 // get lower ids then children. That is, adding a new child does not
133 // change parent's id. This means that, say, adding a new function to a
134 // trait does not change ids of top-level items, which helps caching.
135 bfs(node, |it| {
136 if let Some(module_item) = ast::ModuleItem::cast(it.clone()) {
137 res.alloc(module_item.syntax());
138 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
139 res.alloc(macro_call.syntax());
140 }
141 });
142 res
143 }
144
145 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
146 self.arena.alloc(SyntaxNodePtr::new(item))
147 }
148}
149
150/// Walks the subtree in bfs order, calling `f` for each node.
151fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) {
152 let mut curr_layer = vec![node.clone()];
153 let mut next_layer = vec![];
154 while !curr_layer.is_empty() {
155 curr_layer.drain(..).for_each(|node| {
156 next_layer.extend(node.children());
157 f(node);
158 });
159 std::mem::swap(&mut curr_layer, &mut next_layer);
160 }
161} 73}
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 @@
1[package]
2edition = "2018"
3name = "ra_hir_def"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8ra_arena = { path = "../ra_arena" }
9ra_db = { path = "../ra_db" }
10ra_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 @@
1//! `AstIdMap` allows to create stable IDs for "large" syntax nodes like items
2//! and macro calls.
3//!
4//! Specifically, it enumerates all items in a file and uses position of a an
5//! item as an ID. That way, id's don't change unless the set of items itself
6//! changes.
7
8use std::{
9 hash::{Hash, Hasher},
10 marker::PhantomData,
11 ops,
12};
13
14use ra_arena::{impl_arena_id, Arena, RawId};
15use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr};
16
17/// `AstId` points to an AST node in a specific file.
18#[derive(Debug)]
19pub struct FileAstId<N: AstNode> {
20 raw: ErasedFileAstId,
21 _ty: PhantomData<fn() -> N>,
22}
23
24impl<N: AstNode> Clone for FileAstId<N> {
25 fn clone(&self) -> FileAstId<N> {
26 *self
27 }
28}
29impl<N: AstNode> Copy for FileAstId<N> {}
30
31impl<N: AstNode> PartialEq for FileAstId<N> {
32 fn eq(&self, other: &Self) -> bool {
33 self.raw == other.raw
34 }
35}
36impl<N: AstNode> Eq for FileAstId<N> {}
37impl<N: AstNode> Hash for FileAstId<N> {
38 fn hash<H: Hasher>(&self, hasher: &mut H) {
39 self.raw.hash(hasher);
40 }
41}
42
43impl<N: AstNode> From<FileAstId<N>> for ErasedFileAstId {
44 fn from(id: FileAstId<N>) -> Self {
45 id.raw
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct ErasedFileAstId(RawId);
51impl_arena_id!(ErasedFileAstId);
52
53/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
54#[derive(Debug, PartialEq, Eq, Default)]
55pub struct AstIdMap {
56 arena: Arena<ErasedFileAstId, SyntaxNodePtr>,
57}
58
59impl AstIdMap {
60 pub fn from_source(node: &SyntaxNode) -> AstIdMap {
61 assert!(node.parent().is_none());
62 let mut res = AstIdMap { arena: Arena::default() };
63 // By walking the tree in bread-first order we make sure that parents
64 // get lower ids then children. That is, adding a new child does not
65 // change parent's id. This means that, say, adding a new function to a
66 // trait does not change ids of top-level items, which helps caching.
67 bfs(node, |it| {
68 if let Some(module_item) = ast::ModuleItem::cast(it.clone()) {
69 res.alloc(module_item.syntax());
70 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
71 res.alloc(macro_call.syntax());
72 }
73 });
74 res
75 }
76
77 pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
78 let ptr = SyntaxNodePtr::new(item.syntax());
79 let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) {
80 Some((it, _)) => it,
81 None => panic!(
82 "Can't find {:?} in AstIdMap:\n{:?}",
83 item.syntax(),
84 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
85 ),
86 };
87
88 FileAstId { raw, _ty: PhantomData }
89 }
90
91 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
92 self.arena.alloc(SyntaxNodePtr::new(item))
93 }
94}
95
96impl ops::Index<ErasedFileAstId> for AstIdMap {
97 type Output = SyntaxNodePtr;
98 fn index(&self, index: ErasedFileAstId) -> &SyntaxNodePtr {
99 &self.arena[index]
100 }
101}
102
103/// Walks the subtree in bfs order, calling `f` for each node.
104fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) {
105 let mut curr_layer = vec![node.clone()];
106 let mut next_layer = vec![];
107 while !curr_layer.is_empty() {
108 curr_layer.drain(..).for_each(|node| {
109 next_layer.extend(node.children());
110 f(node);
111 });
112 std::mem::swap(&mut curr_layer, &mut next_layer);
113 }
114}
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 @@
1//! `ra_hir_def` contains initial "phases" of the compiler. Roughly, everything
2//! before types.
3//!
4//! Note that we are in the process of moving parts of `ra_hir` into
5//! `ra_hir_def`, so this crates doesn't contain a lot at the moment.
6
7pub mod ast_id_map;