diff options
author | Aleksey Kladov <[email protected]> | 2019-10-29 08:15:51 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-10-29 08:15:51 +0000 |
commit | 77f90caf2deeb6a2d2c8196399fbba61bf0c461d (patch) | |
tree | 09667ecbdcc8b84916c509d3a3d09ab56b5d4aed /crates | |
parent | 120000609ab0a0e6a946404d0477f5a0a7107800 (diff) |
start ra_hir_def crate
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/expr/lower.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/from_source.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ids.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/impl_block.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir/src/source_id.rs | 132 | ||||
-rw-r--r-- | crates/ra_hir_def/Cargo.toml | 10 | ||||
-rw-r--r-- | crates/ra_hir_def/src/ast_id_map.rs | 114 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 7 |
11 files changed, 171 insertions, 131 deletions
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" } | |||
19 | ra_db = { path = "../ra_db" } | 19 | ra_db = { path = "../ra_db" } |
20 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | 20 | mbe = { path = "../ra_mbe", package = "ra_mbe" } |
21 | tt = { path = "../ra_tt", package = "ra_tt" } | 21 | tt = { path = "../ra_tt", package = "ra_tt" } |
22 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } | ||
22 | test_utils = { path = "../test_utils" } | 23 | test_utils = { path = "../test_utils" } |
23 | ra_prof = { path = "../ra_prof" } | 24 | ra_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)] |
61 | pub trait AstDatabase: InternDatabase { | 61 | pub 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(¯o_call).with_file_id(file_id); | 259 | let ast_id = AstId::new(file_id, db.ast_id_map(file_id).ast_id(¯o_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 | ||
3 | use std::{ | 3 | use std::{ |
4 | hash::{Hash, Hasher}, | 4 | hash::{Hash, Hasher}, |
5 | marker::PhantomData, | ||
6 | sync::Arc, | 5 | sync::Arc, |
7 | }; | 6 | }; |
8 | 7 | ||
9 | use ra_arena::{impl_arena_id, Arena, RawId}; | 8 | pub use hir_def::ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId}; |
10 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; | 9 | use ra_syntax::{AstNode, SyntaxNode}; |
11 | 10 | ||
12 | use crate::{db::AstDatabase, HirFileId}; | 11 | use 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)] |
18 | pub(crate) struct AstId<N: AstNode> { | 18 | pub(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 | ||
42 | impl<N: AstNode> AstId<N> { | 42 | impl<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. | 57 | pub(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) { |
55 | pub(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) | |
60 | impl<N: AstNode> Clone for FileAstId<N> { | ||
61 | fn clone(&self) -> FileAstId<N> { | ||
62 | *self | ||
63 | } | ||
64 | } | ||
65 | impl<N: AstNode> Copy for FileAstId<N> {} | ||
66 | |||
67 | impl<N: AstNode> PartialEq for FileAstId<N> { | ||
68 | fn eq(&self, other: &Self) -> bool { | ||
69 | self.raw == other.raw | ||
70 | } | ||
71 | } | ||
72 | impl<N: AstNode> Eq for FileAstId<N> {} | ||
73 | impl<N: AstNode> Hash for FileAstId<N> { | ||
74 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
75 | self.raw.hash(hasher); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | impl<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)] | ||
86 | pub struct ErasedFileAstId(RawId); | ||
87 | impl_arena_id!(ErasedFileAstId); | ||
88 | |||
89 | /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. | ||
90 | #[derive(Debug, PartialEq, Eq, Default)] | ||
91 | pub struct AstIdMap { | ||
92 | arena: Arena<ErasedFileAstId, SyntaxNodePtr>, | ||
93 | } | 64 | } |
94 | 65 | ||
95 | impl AstIdMap { | 66 | pub(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. | ||
151 | fn 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] | ||
2 | edition = "2018" | ||
3 | name = "ra_hir_def" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | |||
7 | [dependencies] | ||
8 | ra_arena = { path = "../ra_arena" } | ||
9 | ra_db = { path = "../ra_db" } | ||
10 | 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 @@ | |||
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 | |||
8 | use std::{ | ||
9 | hash::{Hash, Hasher}, | ||
10 | marker::PhantomData, | ||
11 | ops, | ||
12 | }; | ||
13 | |||
14 | use ra_arena::{impl_arena_id, Arena, RawId}; | ||
15 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; | ||
16 | |||
17 | /// `AstId` points to an AST node in a specific file. | ||
18 | #[derive(Debug)] | ||
19 | pub struct FileAstId<N: AstNode> { | ||
20 | raw: ErasedFileAstId, | ||
21 | _ty: PhantomData<fn() -> N>, | ||
22 | } | ||
23 | |||
24 | impl<N: AstNode> Clone for FileAstId<N> { | ||
25 | fn clone(&self) -> FileAstId<N> { | ||
26 | *self | ||
27 | } | ||
28 | } | ||
29 | impl<N: AstNode> Copy for FileAstId<N> {} | ||
30 | |||
31 | impl<N: AstNode> PartialEq for FileAstId<N> { | ||
32 | fn eq(&self, other: &Self) -> bool { | ||
33 | self.raw == other.raw | ||
34 | } | ||
35 | } | ||
36 | impl<N: AstNode> Eq for FileAstId<N> {} | ||
37 | impl<N: AstNode> Hash for FileAstId<N> { | ||
38 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
39 | self.raw.hash(hasher); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl<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)] | ||
50 | pub struct ErasedFileAstId(RawId); | ||
51 | impl_arena_id!(ErasedFileAstId); | ||
52 | |||
53 | /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. | ||
54 | #[derive(Debug, PartialEq, Eq, Default)] | ||
55 | pub struct AstIdMap { | ||
56 | arena: Arena<ErasedFileAstId, SyntaxNodePtr>, | ||
57 | } | ||
58 | |||
59 | impl 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 | |||
96 | impl 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. | ||
104 | fn 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 | |||
7 | pub mod ast_id_map; | ||