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