aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/ast_id_map.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-10-29 11:59:55 +0000
committerAleksey Kladov <[email protected]>2019-10-29 11:59:55 +0000
commit5b803055b7b690a57f1c08da8f1640139c739e76 (patch)
treefe8929d0715040bb9200974b88e25f57596a5813 /crates/ra_hir_expand/src/ast_id_map.rs
parent541387564483ee3a42a1969fd048f94e57599ca4 (diff)
rename hir_def -> hir_expand
Diffstat (limited to 'crates/ra_hir_expand/src/ast_id_map.rs')
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs114
1 files changed, 114 insertions, 0 deletions
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
new file mode 100644
index 000000000..c3b389102
--- /dev/null
+++ b/crates/ra_hir_expand/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}