aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/expand.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/expand.rs')
-rw-r--r--crates/ra_hir_expand/src/expand.rs248
1 files changed, 0 insertions, 248 deletions
diff --git a/crates/ra_hir_expand/src/expand.rs b/crates/ra_hir_expand/src/expand.rs
deleted file mode 100644
index 3921175cb..000000000
--- a/crates/ra_hir_expand/src/expand.rs
+++ /dev/null
@@ -1,248 +0,0 @@
1use std::{
2 hash::{Hash, Hasher},
3 sync::Arc,
4};
5
6use mbe::MacroRules;
7use ra_db::{salsa, CrateId, FileId};
8use ra_prof::profile;
9use ra_syntax::{
10 ast::{self, AstNode},
11 Parse, SyntaxNode,
12};
13
14use crate::{ast_id_map::FileAstId, db::AstDatabase};
15
16macro_rules! impl_intern_key {
17 ($name:ident) => {
18 impl salsa::InternKey for $name {
19 fn from_intern_id(v: salsa::InternId) -> Self {
20 $name(v)
21 }
22 fn as_intern_id(&self) -> salsa::InternId {
23 self.0
24 }
25 }
26 };
27}
28
29/// Input to the analyzer is a set of files, where each file is identified by
30/// `FileId` and contains source code. However, another source of source code in
31/// Rust are macros: each macro can be thought of as producing a "temporary
32/// file". To assign an id to such a file, we use the id of the macro call that
33/// produced the file. So, a `HirFileId` is either a `FileId` (source code
34/// written by user), or a `MacroCallId` (source code produced by macro).
35///
36/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
37/// containing the call plus the offset of the macro call in the file. Note that
38/// this is a recursive definition! However, the size_of of `HirFileId` is
39/// finite (because everything bottoms out at the real `FileId`) and small
40/// (`MacroCallId` uses the location interner).
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub struct HirFileId(HirFileIdRepr);
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45enum HirFileIdRepr {
46 FileId(FileId),
47 MacroFile(MacroFile),
48}
49
50impl From<FileId> for HirFileId {
51 fn from(id: FileId) -> Self {
52 HirFileId(HirFileIdRepr::FileId(id))
53 }
54}
55
56impl From<MacroFile> for HirFileId {
57 fn from(id: MacroFile) -> Self {
58 HirFileId(HirFileIdRepr::MacroFile(id))
59 }
60}
61
62impl HirFileId {
63 /// For macro-expansion files, returns the file original source file the
64 /// expansion originated from.
65 pub fn original_file(self, db: &impl AstDatabase) -> FileId {
66 match self.0 {
67 HirFileIdRepr::FileId(file_id) => file_id,
68 HirFileIdRepr::MacroFile(macro_file) => {
69 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
70 loc.ast_id.file_id().original_file(db)
71 }
72 }
73 }
74
75 /// Get the crate which the macro lives in, if it is a macro file.
76 pub fn macro_crate(self, db: &impl AstDatabase) -> Option<CrateId> {
77 match self.0 {
78 HirFileIdRepr::FileId(_) => None,
79 HirFileIdRepr::MacroFile(macro_file) => {
80 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
81 Some(loc.def.krate)
82 }
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88pub struct MacroFile {
89 macro_call_id: MacroCallId,
90 macro_file_kind: MacroFileKind,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
94pub enum MacroFileKind {
95 Items,
96 Expr,
97}
98
99/// `MacroCallId` identifies a particular macro invocation, like
100/// `println!("Hello, {}", world)`.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub struct MacroCallId(salsa::InternId);
103impl_intern_key!(MacroCallId);
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub struct MacroDefId {
107 pub krate: CrateId,
108 pub ast_id: AstId<ast::MacroCall>,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub struct MacroCallLoc {
113 pub def: MacroDefId,
114 pub ast_id: AstId<ast::MacroCall>,
115}
116
117impl MacroCallId {
118 pub fn loc(self, db: &impl AstDatabase) -> MacroCallLoc {
119 db.lookup_intern_macro(self)
120 }
121
122 pub fn as_file(self, kind: MacroFileKind) -> HirFileId {
123 let macro_file = MacroFile { macro_call_id: self, macro_file_kind: kind };
124 macro_file.into()
125 }
126}
127
128impl MacroCallLoc {
129 pub fn id(self, db: &impl AstDatabase) -> MacroCallId {
130 db.intern_macro(self)
131 }
132}
133
134/// `AstId` points to an AST node in any file.
135///
136/// It is stable across reparses, and can be used as salsa key/value.
137// FIXME: isn't this just a `Source<FileAstId<N>>` ?
138#[derive(Debug)]
139pub struct AstId<N: AstNode> {
140 file_id: HirFileId,
141 file_ast_id: FileAstId<N>,
142}
143
144impl<N: AstNode> Clone for AstId<N> {
145 fn clone(&self) -> AstId<N> {
146 *self
147 }
148}
149impl<N: AstNode> Copy for AstId<N> {}
150
151impl<N: AstNode> PartialEq for AstId<N> {
152 fn eq(&self, other: &Self) -> bool {
153 (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id)
154 }
155}
156impl<N: AstNode> Eq for AstId<N> {}
157impl<N: AstNode> Hash for AstId<N> {
158 fn hash<H: Hasher>(&self, hasher: &mut H) {
159 (self.file_id, self.file_ast_id).hash(hasher);
160 }
161}
162
163impl<N: AstNode> AstId<N> {
164 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
165 AstId { file_id, file_ast_id }
166 }
167
168 pub fn file_id(&self) -> HirFileId {
169 self.file_id
170 }
171
172 pub fn to_node(&self, db: &impl AstDatabase) -> N {
173 let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into());
174 N::cast(syntax_node).unwrap()
175 }
176}
177
178pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
179 let macro_call = id.ast_id.to_node(db);
180 let arg = macro_call.token_tree()?;
181 let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
182 log::warn!("fail on macro_def to token tree: {:#?}", arg);
183 None
184 })?;
185 let rules = MacroRules::parse(&tt).ok().or_else(|| {
186 log::warn!("fail on macro_def parse: {:#?}", tt);
187 None
188 })?;
189 Some(Arc::new(rules))
190}
191
192pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> {
193 let loc = db.lookup_intern_macro(id);
194 let macro_call = loc.ast_id.to_node(db);
195 let arg = macro_call.token_tree()?;
196 let (tt, _) = mbe::ast_to_token_tree(&arg)?;
197 Some(Arc::new(tt))
198}
199
200pub(crate) fn macro_expand_query(
201 db: &impl AstDatabase,
202 id: MacroCallId,
203) -> Result<Arc<tt::Subtree>, String> {
204 let loc = db.lookup_intern_macro(id);
205 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
206
207 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
208 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?;
209 // Set a hard limit for the expanded tt
210 let count = tt.count();
211 if count > 65536 {
212 return Err(format!("Total tokens count exceed limit : count = {}", count));
213 }
214 Ok(Arc::new(tt))
215}
216
217pub(crate) fn parse_or_expand_query(
218 db: &impl AstDatabase,
219 file_id: HirFileId,
220) -> Option<SyntaxNode> {
221 match file_id.0 {
222 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
223 HirFileIdRepr::MacroFile(macro_file) => {
224 db.parse_macro(macro_file).map(|it| it.syntax_node())
225 }
226 }
227}
228
229pub(crate) fn parse_macro_query(
230 db: &impl AstDatabase,
231 macro_file: MacroFile,
232) -> Option<Parse<SyntaxNode>> {
233 let _p = profile("parse_macro_query");
234 let macro_call_id = macro_file.macro_call_id;
235 let tt = db
236 .macro_expand(macro_call_id)
237 .map_err(|err| {
238 // Note:
239 // The final goal we would like to make all parse_macro success,
240 // such that the following log will not call anyway.
241 log::warn!("fail on macro_parse: (reason: {})", err,);
242 })
243 .ok()?;
244 match macro_file.macro_file_kind {
245 MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax),
246 MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax),
247 }
248}