aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/source_id.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/source_id.rs')
-rw-r--r--crates/ra_hir/src/source_id.rs157
1 files changed, 97 insertions, 60 deletions
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs
index 62707ba6a..0a8fb6d32 100644
--- a/crates/ra_hir/src/source_id.rs
+++ b/crates/ra_hir/src/source_id.rs
@@ -1,59 +1,122 @@
1use std::sync::Arc; 1use std::{marker::PhantomData, sync::Arc, hash::{Hash, Hasher}};
2 2
3use ra_arena::{Arena, RawId, impl_arena_id}; 3use ra_arena::{Arena, RawId, impl_arena_id};
4use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; 4use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast};
5 5
6use crate::{HirFileId, DefDatabase}; 6use crate::{HirFileId, DefDatabase};
7 7
8/// Identifier of item within a specific file. This is stable over reparses, so 8/// `AstId` points to an AST node in any file.
9/// it's OK to use it as a salsa key/value. 9///
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 10/// It is stable across reparses, and can be used as salsa key/value.
11pub(crate) struct SourceFileItemId(RawId); 11#[derive(Debug)]
12impl_arena_id!(SourceFileItemId); 12pub(crate) struct AstId<N: AstNode> {
13 file_id: HirFileId,
14 file_ast_id: FileAstId<N>,
15}
16
17impl<N: AstNode> Clone for AstId<N> {
18 fn clone(&self) -> AstId<N> {
19 *self
20 }
21}
22impl<N: AstNode> Copy for AstId<N> {}
23
24impl<N: AstNode> PartialEq for AstId<N> {
25 fn eq(&self, other: &Self) -> bool {
26 (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
27 }
28}
29impl<N: AstNode> Eq for AstId<N> {}
30impl<N: AstNode> Hash for AstId<N> {
31 fn hash<H: Hasher>(&self, hasher: &mut H) {
32 (self.file_id, self.file_ast_id).hash(hasher);
33 }
34}
35
36impl<N: AstNode> AstId<N> {
37 pub(crate) fn file_id(&self) -> HirFileId {
38 self.file_id
39 }
40
41 pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc<N> {
42 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw);
43 N::cast(&syntax_node).unwrap().to_owned()
44 }
45}
46
47/// `AstId` points to an AST node in a specific file.
48#[derive(Debug)]
49pub(crate) struct FileAstId<N: AstNode> {
50 raw: ErasedFileAstId,
51 _ty: PhantomData<N>,
52}
53
54impl<N: AstNode> Clone for FileAstId<N> {
55 fn clone(&self) -> FileAstId<N> {
56 *self
57 }
58}
59impl<N: AstNode> Copy for FileAstId<N> {}
13 60
14impl SourceFileItemId { 61impl<N: AstNode> PartialEq for FileAstId<N> {
15 pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { 62 fn eq(&self, other: &Self) -> bool {
16 SourceItemId { file_id, item_id: self } 63 self.raw == other.raw
64 }
65}
66impl<N: AstNode> Eq for FileAstId<N> {}
67impl<N: AstNode> Hash for FileAstId<N> {
68 fn hash<H: Hasher>(&self, hasher: &mut H) {
69 self.raw.hash(hasher);
17 } 70 }
18} 71}
19 72
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 73impl<N: AstNode> FileAstId<N> {
21pub struct SourceItemId { 74 pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> {
22 pub(crate) file_id: HirFileId, 75 AstId { file_id, file_ast_id: self }
23 pub(crate) item_id: SourceFileItemId, 76 }
24} 77}
25 78
26/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. 79#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub struct ErasedFileAstId(RawId);
81impl_arena_id!(ErasedFileAstId);
82
83/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
27#[derive(Debug, PartialEq, Eq)] 84#[derive(Debug, PartialEq, Eq)]
28pub struct SourceFileItems { 85pub struct AstIdMap {
29 file_id: HirFileId, 86 arena: Arena<ErasedFileAstId, SyntaxNodePtr>,
30 arena: Arena<SourceFileItemId, SyntaxNodePtr>,
31} 87}
32 88
33impl SourceFileItems { 89impl AstIdMap {
34 pub(crate) fn file_items_query( 90 pub(crate) fn ast_id_map_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
35 db: &impl DefDatabase,
36 file_id: HirFileId,
37 ) -> Arc<SourceFileItems> {
38 let source_file = db.hir_parse(file_id); 91 let source_file = db.hir_parse(file_id);
39 Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) 92 Arc::new(AstIdMap::from_source_file(&source_file))
40 } 93 }
41 94
42 pub(crate) fn file_item_query( 95 pub(crate) fn file_item_query(
43 db: &impl DefDatabase, 96 db: &impl DefDatabase,
44 source_item_id: SourceItemId, 97 file_id: HirFileId,
98 ast_id: ErasedFileAstId,
45 ) -> TreeArc<SyntaxNode> { 99 ) -> TreeArc<SyntaxNode> {
46 let source_file = db.hir_parse(source_item_id.file_id); 100 let source_file = db.hir_parse(file_id);
47 db.file_items(source_item_id.file_id)[source_item_id.item_id] 101 db.ast_id_map(file_id).arena[ast_id].to_node(&source_file).to_owned()
48 .to_node(&source_file)
49 .to_owned()
50 } 102 }
51 103
52 pub(crate) fn from_source_file( 104 pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
53 source_file: &SourceFile, 105 let ptr = SyntaxNodePtr::new(item.syntax());
54 file_id: HirFileId, 106 let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) {
55 ) -> SourceFileItems { 107 Some((it, _)) => it,
56 let mut res = SourceFileItems { file_id, arena: Arena::default() }; 108 None => panic!(
109 "Can't find {:?} in AstIdMap:\n{:?}",
110 item.syntax(),
111 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
112 ),
113 };
114
115 FileAstId { raw, _ty: PhantomData }
116 }
117
118 fn from_source_file(source_file: &SourceFile) -> AstIdMap {
119 let mut res = AstIdMap { arena: Arena::default() };
57 // By walking the tree in bread-first order we make sure that parents 120 // By walking the tree in bread-first order we make sure that parents
58 // get lower ids then children. That is, adding a new child does not 121 // get lower ids then children. That is, adding a new child does not
59 // change parent's id. This means that, say, adding a new function to a 122 // change parent's id. This means that, say, adding a new function to a
@@ -68,35 +131,9 @@ impl SourceFileItems {
68 res 131 res
69 } 132 }
70 133
71 fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { 134 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
72 self.arena.alloc(SyntaxNodePtr::new(item)) 135 self.arena.alloc(SyntaxNodePtr::new(item))
73 } 136 }
74 pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId {
75 assert_eq!(
76 self.file_id, file_id,
77 "SourceFileItems: wrong file, expected {:?}, got {:?}",
78 self.file_id, file_id
79 );
80 self.id_of_unchecked(item)
81 }
82 pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId {
83 let ptr = SyntaxNodePtr::new(item);
84 if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) {
85 return id;
86 }
87 panic!(
88 "Can't find {:?} in SourceFileItems:\n{:?}",
89 item,
90 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
91 );
92 }
93}
94
95impl std::ops::Index<SourceFileItemId> for SourceFileItems {
96 type Output = SyntaxNodePtr;
97 fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr {
98 &self.arena[idx]
99 }
100} 137}
101 138
102/// Walks the subtree in bfs order, calling `f` for each node. 139/// Walks the subtree in bfs order, calling `f` for each node.