aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/lib.rs')
-rw-r--r--crates/ra_hir_expand/src/lib.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
new file mode 100644
index 000000000..6b3538673
--- /dev/null
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -0,0 +1,161 @@
1//! `ra_hir_expand` deals with macro expansion.
2//!
3//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
4//! tree originates not from the text of some `FileId`, but from some macro
5//! expansion.
6
7pub mod db;
8pub mod ast_id_map;
9
10use std::hash::{Hash, Hasher};
11
12use ra_db::{salsa, CrateId, FileId};
13use ra_syntax::ast::{self, AstNode};
14
15use crate::{ast_id_map::FileAstId, db::AstDatabase};
16
17/// Input to the analyzer is a set of files, where each file is identified by
18/// `FileId` and contains source code. However, another source of source code in
19/// Rust are macros: each macro can be thought of as producing a "temporary
20/// file". To assign an id to such a file, we use the id of the macro call that
21/// produced the file. So, a `HirFileId` is either a `FileId` (source code
22/// written by user), or a `MacroCallId` (source code produced by macro).
23///
24/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
25/// containing the call plus the offset of the macro call in the file. Note that
26/// this is a recursive definition! However, the size_of of `HirFileId` is
27/// finite (because everything bottoms out at the real `FileId`) and small
28/// (`MacroCallId` uses the location interner).
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub struct HirFileId(HirFileIdRepr);
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33enum HirFileIdRepr {
34 FileId(FileId),
35 MacroFile(MacroFile),
36}
37
38impl From<FileId> for HirFileId {
39 fn from(id: FileId) -> Self {
40 HirFileId(HirFileIdRepr::FileId(id))
41 }
42}
43
44impl From<MacroFile> for HirFileId {
45 fn from(id: MacroFile) -> Self {
46 HirFileId(HirFileIdRepr::MacroFile(id))
47 }
48}
49
50impl HirFileId {
51 /// For macro-expansion files, returns the file original source file the
52 /// expansion originated from.
53 pub fn original_file(self, db: &dyn AstDatabase) -> FileId {
54 match self.0 {
55 HirFileIdRepr::FileId(file_id) => file_id,
56 HirFileIdRepr::MacroFile(macro_file) => {
57 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
58 loc.ast_id.file_id().original_file(db)
59 }
60 }
61 }
62
63 /// Get the crate which the macro lives in, if it is a macro file.
64 pub fn macro_crate(self, db: &dyn AstDatabase) -> Option<CrateId> {
65 match self.0 {
66 HirFileIdRepr::FileId(_) => None,
67 HirFileIdRepr::MacroFile(macro_file) => {
68 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
69 Some(loc.def.krate)
70 }
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
76pub struct MacroFile {
77 macro_call_id: MacroCallId,
78 macro_file_kind: MacroFileKind,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
82pub enum MacroFileKind {
83 Items,
84 Expr,
85}
86
87/// `MacroCallId` identifies a particular macro invocation, like
88/// `println!("Hello, {}", world)`.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub struct MacroCallId(salsa::InternId);
91impl salsa::InternKey for MacroCallId {
92 fn from_intern_id(v: salsa::InternId) -> Self {
93 MacroCallId(v)
94 }
95 fn as_intern_id(&self) -> salsa::InternId {
96 self.0
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101pub struct MacroDefId {
102 pub krate: CrateId,
103 pub ast_id: AstId<ast::MacroCall>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Hash)]
107pub struct MacroCallLoc {
108 pub def: MacroDefId,
109 pub ast_id: AstId<ast::MacroCall>,
110}
111
112impl MacroCallId {
113 pub fn as_file(self, kind: MacroFileKind) -> HirFileId {
114 let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
115 macro_file.into()
116 }
117}
118
119/// `AstId` points to an AST node in any file.
120///
121/// It is stable across reparses, and can be used as salsa key/value.
122// FIXME: isn't this just a `Source<FileAstId<N>>` ?
123#[derive(Debug)]
124pub struct AstId<N: AstNode> {
125 file_id: HirFileId,
126 file_ast_id: FileAstId<N>,
127}
128
129impl<N: AstNode> Clone for AstId<N> {
130 fn clone(&self) -> AstId<N> {
131 *self
132 }
133}
134impl<N: AstNode> Copy for AstId<N> {}
135
136impl<N: AstNode> PartialEq for AstId<N> {
137 fn eq(&self, other: &Self) -> bool {
138 (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
139 }
140}
141impl<N: AstNode> Eq for AstId<N> {}
142impl<N: AstNode> Hash for AstId<N> {
143 fn hash<H: Hasher>(&self, hasher: &mut H) {
144 (self.file_id, self.file_ast_id).hash(hasher);
145 }
146}
147
148impl<N: AstNode> AstId<N> {
149 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
150 AstId { file_id, file_ast_id }
151 }
152
153 pub fn file_id(&self) -> HirFileId {
154 self.file_id
155 }
156
157 pub fn to_node(&self, db: &dyn AstDatabase) -> N {
158 let root = db.parse_or_expand(self.file_id).unwrap();
159 db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root)
160 }
161}