diff options
Diffstat (limited to 'crates/ra_hir_def')
-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 |
3 files changed, 131 insertions, 0 deletions
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; | ||