aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-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
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]
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;