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.rs287
1 files changed, 287 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..4c7ebe3ea
--- /dev/null
+++ b/crates/ra_hir/src/ids.rs
@@ -0,0 +1,287 @@
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 as_macro_call_id(self) -> Option<MacroCallId> {
52 match self.0 {
53 HirFileIdRepr::Macro(it) => Some(it),
54 _ => None,
55 }
56 }
57
58 pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode {
59 match file_id.0 {
60 HirFileIdRepr::File(file_id) => db.source_file(file_id),
61 HirFileIdRepr::Macro(m) => {
62 if let Some(exp) = db.expand_macro_invocation(m) {
63 return exp.file();
64 }
65 // returning an empty string looks fishy...
66 SourceFileNode::parse("")
67 }
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73enum HirFileIdRepr {
74 File(FileId),
75 Macro(MacroCallId),
76}
77
78impl From<FileId> for HirFileId {
79 fn from(file_id: FileId) -> HirFileId {
80 HirFileId(HirFileIdRepr::File(file_id))
81 }
82}
83
84impl From<MacroCallId> for HirFileId {
85 fn from(macro_call_id: MacroCallId) -> HirFileId {
86 HirFileId(HirFileIdRepr::Macro(macro_call_id))
87 }
88}
89
90/// `MacroCallId` identifies a particular macro invocation, like
91/// `println!("Hello, {}", world)`.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93pub struct MacroCallId(u32);
94ra_db::impl_numeric_id!(MacroCallId);
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub struct MacroCallLoc {
98 pub(crate) source_root_id: SourceRootId,
99 pub(crate) module_id: ModuleId,
100 pub(crate) source_item_id: SourceItemId,
101}
102
103impl MacroCallId {
104 pub(crate) fn loc(
105 self,
106 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
107 ) -> MacroCallLoc {
108 db.as_ref().id2loc(self)
109 }
110}
111
112impl MacroCallLoc {
113 #[allow(unused)]
114 pub(crate) fn id(
115 &self,
116 db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>,
117 ) -> MacroCallId {
118 db.as_ref().loc2id(&self)
119 }
120}
121
122/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
123/// in a specific module.
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub struct DefId(u32);
126ra_db::impl_numeric_id!(DefId);
127
128#[derive(Clone, Debug, PartialEq, Eq, Hash)]
129pub struct DefLoc {
130 pub(crate) kind: DefKind,
131 pub(crate) source_root_id: SourceRootId,
132 pub(crate) module_id: ModuleId,
133 pub(crate) source_item_id: SourceItemId,
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
137pub(crate) enum DefKind {
138 Module,
139 Function,
140 Struct,
141 Enum,
142 Item,
143
144 StructCtor,
145}
146
147impl DefId {
148 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
149 db.as_ref().id2loc(self)
150 }
151
152 pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> {
153 let loc = self.loc(db);
154 let res = match loc.kind {
155 DefKind::Module => {
156 let module = Module::new(db, loc.source_root_id, loc.module_id)?;
157 Def::Module(module)
158 }
159 DefKind::Function => {
160 let function = Function::new(self);
161 Def::Function(function)
162 }
163 DefKind::Struct => {
164 let struct_def = Struct::new(self);
165 Def::Struct(struct_def)
166 }
167 DefKind::Enum => {
168 let enum_def = Enum::new(self);
169 Def::Enum(enum_def)
170 }
171 DefKind::StructCtor => Def::Item,
172 DefKind::Item => Def::Item,
173 };
174 Ok(res)
175 }
176
177 /// For a module, returns that module; for any other def, returns the containing module.
178 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
179 let loc = self.loc(db);
180 Module::new(db, loc.source_root_id, loc.module_id)
181 }
182}
183
184impl DefLoc {
185 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
186 db.as_ref().loc2id(&self)
187 }
188}
189
190impl DefKind {
191 pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> {
192 match kind {
193 SyntaxKind::FN_DEF => PerNs::values(DefKind::Function),
194 SyntaxKind::MODULE => PerNs::types(DefKind::Module),
195 SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor),
196 SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum),
197 // These define items, but don't have their own DefKinds yet:
198 SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item),
199 SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item),
200 SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item),
201 SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item),
202 _ => PerNs::none(),
203 }
204 }
205}
206
207/// Identifier of item within a specific file. This is stable over reparses, so
208/// it's OK to use it as a salsa key/value.
209pub(crate) type SourceFileItemId = Id<SyntaxNode>;
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
212pub struct SourceItemId {
213 pub(crate) file_id: HirFileId,
214 /// None for the whole file.
215 pub(crate) item_id: Option<SourceFileItemId>,
216}
217
218/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
219#[derive(Debug, PartialEq, Eq)]
220pub struct SourceFileItems {
221 file_id: HirFileId,
222 arena: Arena<SyntaxNode>,
223}
224
225impl SourceFileItems {
226 pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems {
227 let mut res = SourceFileItems {
228 file_id,
229 arena: Arena::default(),
230 };
231 res.init(source_file);
232 res
233 }
234
235 fn init(&mut self, source_file: SourceFile) {
236 source_file.syntax().descendants().for_each(|it| {
237 if let Some(module_item) = ast::ModuleItem::cast(it) {
238 self.alloc(module_item.syntax().owned());
239 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
240 self.alloc(macro_call.syntax().owned());
241 }
242 });
243 }
244
245 fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId {
246 self.arena.alloc(item)
247 }
248 pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId {
249 assert_eq!(
250 self.file_id, file_id,
251 "SourceFileItems: wrong file, expected {:?}, got {:?}",
252 self.file_id, file_id
253 );
254 self.id_of_unchecked(item)
255 }
256 pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId {
257 if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) {
258 return id;
259 }
260 // This should not happen. Let's try to give a sensible diagnostics.
261 if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
262 // FIXME(#288): whyyy are we getting here?
263 log::error!(
264 "unequal syntax nodes with the same range:\n{:?}\n{:?}",
265 item,
266 i
267 );
268 return id;
269 }
270 panic!(
271 "Can't find {:?} in SourceFileItems:\n{:?}",
272 item,
273 self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
274 );
275 }
276 pub fn id_of_source_file(&self) -> SourceFileItemId {
277 let (id, _syntax) = self.arena.iter().next().unwrap();
278 id
279 }
280}
281
282impl std::ops::Index<SourceFileItemId> for SourceFileItems {
283 type Output = SyntaxNode;
284 fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
285 &self.arena[idx]
286 }
287}