aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ids.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ids.rs')
-rw-r--r--crates/ra_hir/src/ids.rs280
1 files changed, 280 insertions, 0 deletions
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
new file mode 100644
index 000000000..a09dee8b1
--- /dev/null
+++ b/crates/ra_hir/src/ids.rs
@@ -0,0 +1,280 @@
1use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3
4use crate::{
5 HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum,
6 arena::{Arena, Id},
7};
8
9/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
10/// can think of id as a pointer (but without a lifetime) or a file descriptor
11/// (but for hir objects).
12///
13/// This module defines a bunch of ids we are using. The most important ones are
14/// probably `HirFileId` and `DefId`.
15
16/// Input to the analyzer is a set of file, where each file is indetified by
17/// `FileId` and contains source code. However, another source of source code in
18/// Rust are macros: each macro can be thought of as producing a "temporary
19/// file". To assign id to such file, we use the id of a macro call that
20/// produced the file. So, a `HirFileId` is either a `FileId` (source code
21/// written by user), or a `MacroCallId` (source code produced by macro).
22///
23/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
24/// the call plus the offset of the macro call in the file. Note that this is a
25/// recursive definition! Nethetheless, size_of of `HirFileId` is finite
26/// (because everything bottoms out at the real `FileId`) and small
27/// (`MacroCallId` uses location interner).
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub struct HirFileId(HirFileIdRepr);
30
31impl HirFileId {
32 /// For macro-expansion files, returns the file original source file the
33 /// expansionoriginated from.
34 pub(crate) fn original_file(self, db: &impl HirDatabase) -> FileId {
35 match self.0 {
36 HirFileIdRepr::File(file_id) => file_id,
37 HirFileIdRepr::Macro(macro_call_id) => {
38 let loc = macro_call_id.loc(db);
39 loc.source_item_id.file_id.original_file(db)
40 }
41 }
42 }
43
44 pub(crate) fn as_original_file(self) -> FileId {
45 match self.0 {
46 HirFileIdRepr::File(file_id) => file_id,
47 HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
48 }
49 }
50
51 pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode {
52 match file_id.0 {
53 HirFileIdRepr::File(file_id) => db.source_file(file_id),
54 HirFileIdRepr::Macro(m) => {
55 if let Some(exp) = db.expand_macro_invocation(m) {
56 return exp.file();
57 }
58 // returning an empty string looks fishy...
59 SourceFileNode::parse("")
60 }
61 }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66enum HirFileIdRepr {
67 File(FileId),
68 Macro(MacroCallId),
69}
70
71impl From<FileId> for HirFileId {
72 fn from(file_id: FileId) -> HirFileId {
73 HirFileId(HirFileIdRepr::File(file_id))
74 }
75}
76
77impl From<MacroCallId> for HirFileId {
78 fn from(macro_call_id: MacroCallId) -> HirFileId {
79 HirFileId(HirFileIdRepr::Macro(macro_call_id))
80 }
81}
82
83/// `MacroCallId` identifies a particular macro invocation, like
84/// `println!("Hello, {}", world)`.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub struct MacroCallId(u32);
87ra_db::impl_numeric_id!(MacroCallId);
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub struct MacroCallLoc {
91 pub(crate) source_root_id: SourceRootId,
92 pub(crate) module_id: ModuleId,
93 pub(crate) source_item_id: SourceItemId,
94}
95
96impl MacroCallId {
97 pub(crate) fn loc(
98 self,
99 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
100 ) -> MacroCallLoc {
101 db.as_ref().id2loc(self)
102 }
103}
104
105impl MacroCallLoc {
106 #[allow(unused)]
107 pub(crate) fn id(
108 &self,
109 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
110 ) -> MacroCallId {
111 db.as_ref().loc2id(&self)
112 }
113}
114
115/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
116/// in a specific module.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct DefId(u32);
119ra_db::impl_numeric_id!(DefId);
120
121#[derive(Clone, Debug, PartialEq, Eq, Hash)]
122pub struct DefLoc {
123 pub(crate) kind: DefKind,
124 pub(crate) source_root_id: SourceRootId,
125 pub(crate) module_id: ModuleId,
126 pub(crate) source_item_id: SourceItemId,
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
130pub(crate) enum DefKind {
131 Module,
132 Function,
133 Struct,
134 Enum,
135 Item,
136
137 StructCtor,
138}
139
140impl DefId {
141 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
142 db.as_ref().id2loc(self)
143 }
144
145 pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
146 let loc = self.loc(db);
147 let res = match loc.kind {
148 DefKind::Module => {
149 let module = Module::new(db, loc.source_root_id, loc.module_id)?;
150 Def::Module(module)
151 }
152 DefKind::Function => {
153 let function = Function::new(self);
154 Def::Function(function)
155 }
156 DefKind::Struct => {
157 let struct_def = Struct::new(self);
158 Def::Struct(struct_def)
159 }
160 DefKind::Enum => {
161 let enum_def = Enum::new(self);
162 Def::Enum(enum_def)
163 }
164 DefKind::StructCtor => Def::Item,
165 DefKind::Item => Def::Item,
166 };
167 Ok(res)
168 }
169
170 /// For a module, returns that module; for any other def, returns the containing module.
171 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
172 let loc = self.loc(db);
173 Module::new(db, loc.source_root_id, loc.module_id)
174 }
175}
176
177impl DefLoc {
178 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
179 db.as_ref().loc2id(&self)
180 }
181}
182
183impl DefKind {
184 pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
185 match kind {
186 SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
187 SyntaxKind::MODULE => PerNs::types(DefKind::Module),
188 SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
189 SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
190 // These define items, but don't have their own DefKinds yet:
191 SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
192 SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
193 SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
194 SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
195 _ => PerNs::none(),
196 }
197 }
198}
199
200/// Identifier of item within a specific file. This is stable over reparses, so
201/// it's OK to use it as a salsa key/value.
202pub(crate) type SourceFileItemId = Id<SyntaxNode>;
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205pub struct SourceItemId {
206 pub(crate) file_id: HirFileId,
207 /// None for the whole file.
208 pub(crate) item_id: Option<SourceFileItemId>,
209}
210
211/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
212#[derive(Debug, PartialEq, Eq)]
213pub struct SourceFileItems {
214 file_id: HirFileId,
215 arena: Arena<SyntaxNode>,
216}
217
218impl SourceFileItems {
219 pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems {
220 let mut res = SourceFileItems {
221 file_id,
222 arena: Arena::default(),
223 };
224 res.init(source_file);
225 res
226 }
227
228 fn init(&mut self, source_file: SourceFile) {
229 source_file.syntax().descendants().for_each(|it| {
230 if let Some(module_item) = ast::ModuleItem::cast(it) {
231 self.alloc(module_item.syntax().owned());
232 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
233 self.alloc(macro_call.syntax().owned());
234 }
235 });
236 }
237
238 fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
239 self.arena.alloc(item)
240 }
241 pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId {
242 assert_eq!(
243 self.file_id, file_id,
244 "SourceFileItems: wrong file, expected {:?}, got {:?}",
245 self.file_id, file_id
246 );
247 self.id_of_unchecked(item)
248 }
249 pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
250 if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
251 return id;
252 }
253 // This should not happen. Let's try to give a sensible diagnostics.
254 if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
255 // FIXME(#288): whyyy are we getting here?
256 log::error!(
257 "unequal syntax nodes with the same range:\n{:?}\n{:?}",
258 item,
259 i
260 );
261 return id;
262 }
263 panic!(
264 "Can't find {:?} in SourceFileItems:\n{:?}",
265 item,
266 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
267 );
268 }
269 pub fn id_of_source_file(&self) -> SourceFileItemId {
270 let (id, _syntax) = self.arena.iter().next().unwrap();
271 id
272 }
273}
274
275impl std::ops::Index<SourceFileItemId> for SourceFileItems {
276 type Output = SyntaxNode;
277 fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
278 &self.arena[idx]
279 }
280}