diff options
Diffstat (limited to 'crates/ra_hir_expand/src/db.rs')
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 404 |
1 files changed, 0 insertions, 404 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs deleted file mode 100644 index 41df66696..000000000 --- a/crates/ra_hir_expand/src/db.rs +++ /dev/null | |||
@@ -1,404 +0,0 @@ | |||
1 | //! Defines database & queries for macro expansion. | ||
2 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use mbe::{ExpandResult, MacroRules}; | ||
6 | use ra_db::{salsa, SourceDatabase}; | ||
7 | use ra_parser::FragmentKind; | ||
8 | use ra_prof::profile; | ||
9 | use ra_syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | ||
10 | |||
11 | use crate::{ | ||
12 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, | ||
13 | HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, | ||
14 | MacroFile, ProcMacroExpander, | ||
15 | }; | ||
16 | |||
17 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
18 | pub enum TokenExpander { | ||
19 | MacroRules(mbe::MacroRules), | ||
20 | Builtin(BuiltinFnLikeExpander), | ||
21 | BuiltinDerive(BuiltinDeriveExpander), | ||
22 | ProcMacro(ProcMacroExpander), | ||
23 | } | ||
24 | |||
25 | impl TokenExpander { | ||
26 | pub fn expand( | ||
27 | &self, | ||
28 | db: &dyn AstDatabase, | ||
29 | id: LazyMacroId, | ||
30 | tt: &tt::Subtree, | ||
31 | ) -> mbe::ExpandResult<tt::Subtree> { | ||
32 | match self { | ||
33 | TokenExpander::MacroRules(it) => it.expand(tt), | ||
34 | // FIXME switch these to ExpandResult as well | ||
35 | TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), | ||
36 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | ||
37 | TokenExpander::ProcMacro(_) => { | ||
38 | // We store the result in salsa db to prevent non-determinisc behavior in | ||
39 | // some proc-macro implementation | ||
40 | // See #4315 for details | ||
41 | db.expand_proc_macro(id.into()).into() | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { | ||
47 | match self { | ||
48 | TokenExpander::MacroRules(it) => it.map_id_down(id), | ||
49 | TokenExpander::Builtin(..) => id, | ||
50 | TokenExpander::BuiltinDerive(..) => id, | ||
51 | TokenExpander::ProcMacro(..) => id, | ||
52 | } | ||
53 | } | ||
54 | |||
55 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { | ||
56 | match self { | ||
57 | TokenExpander::MacroRules(it) => it.map_id_up(id), | ||
58 | TokenExpander::Builtin(..) => (id, mbe::Origin::Call), | ||
59 | TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), | ||
60 | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | // FIXME: rename to ExpandDatabase | ||
66 | #[salsa::query_group(AstDatabaseStorage)] | ||
67 | pub trait AstDatabase: SourceDatabase { | ||
68 | fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; | ||
69 | |||
70 | #[salsa::transparent] | ||
71 | fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; | ||
72 | |||
73 | #[salsa::interned] | ||
74 | fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId; | ||
75 | fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>; | ||
76 | #[salsa::transparent] | ||
77 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | ||
78 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | ||
79 | fn parse_macro(&self, macro_file: MacroFile) | ||
80 | -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | ||
81 | fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); | ||
82 | |||
83 | #[salsa::interned] | ||
84 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | ||
85 | |||
86 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | ||
87 | } | ||
88 | |||
89 | /// This expands the given macro call, but with different arguments. This is | ||
90 | /// used for completion, where we want to see what 'would happen' if we insert a | ||
91 | /// token. The `token_to_map` mapped down into the expansion, with the mapped | ||
92 | /// token returned. | ||
93 | pub fn expand_hypothetical( | ||
94 | db: &dyn AstDatabase, | ||
95 | actual_macro_call: MacroCallId, | ||
96 | hypothetical_args: &ra_syntax::ast::TokenTree, | ||
97 | token_to_map: ra_syntax::SyntaxToken, | ||
98 | ) -> Option<(SyntaxNode, ra_syntax::SyntaxToken)> { | ||
99 | let macro_file = MacroFile { macro_call_id: actual_macro_call }; | ||
100 | let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax()).unwrap(); | ||
101 | let range = | ||
102 | token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?; | ||
103 | let token_id = tmap_1.token_by_range(range)?; | ||
104 | let macro_def = expander(db, actual_macro_call)?; | ||
105 | let (node, tmap_2) = | ||
106 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | ||
107 | let token_id = macro_def.0.map_id_down(token_id); | ||
108 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | ||
109 | let token = ra_syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | ||
110 | Some((node.syntax_node(), token)) | ||
111 | } | ||
112 | |||
113 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | ||
114 | let map = | ||
115 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | ||
116 | Arc::new(map) | ||
117 | } | ||
118 | |||
119 | pub(crate) fn macro_def( | ||
120 | db: &dyn AstDatabase, | ||
121 | id: MacroDefId, | ||
122 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
123 | match id.kind { | ||
124 | MacroDefKind::Declarative => { | ||
125 | let macro_call = id.ast_id?.to_node(db); | ||
126 | let arg = macro_call.token_tree()?; | ||
127 | let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { | ||
128 | log::warn!("fail on macro_def to token tree: {:#?}", arg); | ||
129 | None | ||
130 | })?; | ||
131 | let rules = match MacroRules::parse(&tt) { | ||
132 | Ok(it) => it, | ||
133 | Err(err) => { | ||
134 | log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt); | ||
135 | return None; | ||
136 | } | ||
137 | }; | ||
138 | Some(Arc::new((TokenExpander::MacroRules(rules), tmap))) | ||
139 | } | ||
140 | MacroDefKind::BuiltIn(expander) => { | ||
141 | Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default()))) | ||
142 | } | ||
143 | MacroDefKind::BuiltInDerive(expander) => { | ||
144 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) | ||
145 | } | ||
146 | MacroDefKind::BuiltInEager(_) => None, | ||
147 | MacroDefKind::CustomDerive(expander) => { | ||
148 | Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | ||
154 | let id = match id { | ||
155 | MacroCallId::LazyMacro(id) => id, | ||
156 | MacroCallId::EagerMacro(_id) => { | ||
157 | // FIXME: support macro_arg for eager macro | ||
158 | return None; | ||
159 | } | ||
160 | }; | ||
161 | let loc = db.lookup_intern_macro(id); | ||
162 | let arg = loc.kind.arg(db)?; | ||
163 | Some(arg.green().clone()) | ||
164 | } | ||
165 | |||
166 | pub(crate) fn macro_arg( | ||
167 | db: &dyn AstDatabase, | ||
168 | id: MacroCallId, | ||
169 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
170 | let arg = db.macro_arg_text(id)?; | ||
171 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; | ||
172 | Some(Arc::new((tt, tmap))) | ||
173 | } | ||
174 | |||
175 | pub(crate) fn macro_expand( | ||
176 | db: &dyn AstDatabase, | ||
177 | id: MacroCallId, | ||
178 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | ||
179 | macro_expand_with_arg(db, id, None) | ||
180 | } | ||
181 | |||
182 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
183 | let lazy_id = match id { | ||
184 | MacroCallId::LazyMacro(id) => id, | ||
185 | MacroCallId::EagerMacro(_id) => { | ||
186 | return None; | ||
187 | } | ||
188 | }; | ||
189 | |||
190 | let loc = db.lookup_intern_macro(lazy_id); | ||
191 | let macro_rules = db.macro_def(loc.def)?; | ||
192 | Some(macro_rules) | ||
193 | } | ||
194 | |||
195 | fn macro_expand_with_arg( | ||
196 | db: &dyn AstDatabase, | ||
197 | id: MacroCallId, | ||
198 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
199 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | ||
200 | let lazy_id = match id { | ||
201 | MacroCallId::LazyMacro(id) => id, | ||
202 | MacroCallId::EagerMacro(id) => { | ||
203 | if arg.is_some() { | ||
204 | return ( | ||
205 | None, | ||
206 | Some("hypothetical macro expansion not implemented for eager macro".to_owned()), | ||
207 | ); | ||
208 | } else { | ||
209 | return (Some(db.lookup_intern_eager_expansion(id).subtree), None); | ||
210 | } | ||
211 | } | ||
212 | }; | ||
213 | |||
214 | let loc = db.lookup_intern_macro(lazy_id); | ||
215 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { | ||
216 | Some(it) => it, | ||
217 | None => return (None, Some("Fail to args in to tt::TokenTree".into())), | ||
218 | }; | ||
219 | |||
220 | let macro_rules = match db.macro_def(loc.def) { | ||
221 | Some(it) => it, | ||
222 | None => return (None, Some("Fail to find macro definition".into())), | ||
223 | }; | ||
224 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | ||
225 | // Set a hard limit for the expanded tt | ||
226 | let count = tt.count(); | ||
227 | if count > 65536 { | ||
228 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); | ||
229 | } | ||
230 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | ||
231 | } | ||
232 | |||
233 | pub(crate) fn expand_proc_macro( | ||
234 | db: &dyn AstDatabase, | ||
235 | id: MacroCallId, | ||
236 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
237 | let lazy_id = match id { | ||
238 | MacroCallId::LazyMacro(id) => id, | ||
239 | MacroCallId::EagerMacro(_) => unreachable!(), | ||
240 | }; | ||
241 | |||
242 | let loc = db.lookup_intern_macro(lazy_id); | ||
243 | let macro_arg = match db.macro_arg(id) { | ||
244 | Some(it) => it, | ||
245 | None => { | ||
246 | return Err( | ||
247 | tt::ExpansionError::Unknown("No arguments for proc-macro".to_string()).into() | ||
248 | ) | ||
249 | } | ||
250 | }; | ||
251 | |||
252 | let expander = match loc.def.kind { | ||
253 | MacroDefKind::CustomDerive(expander) => expander, | ||
254 | _ => unreachable!(), | ||
255 | }; | ||
256 | |||
257 | expander.expand(db, lazy_id, ¯o_arg.0) | ||
258 | } | ||
259 | |||
260 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | ||
261 | match file_id.0 { | ||
262 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | ||
263 | HirFileIdRepr::MacroFile(macro_file) => { | ||
264 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | pub(crate) fn parse_macro( | ||
270 | db: &dyn AstDatabase, | ||
271 | macro_file: MacroFile, | ||
272 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | ||
273 | parse_macro_with_arg(db, macro_file, None) | ||
274 | } | ||
275 | |||
276 | pub fn parse_macro_with_arg( | ||
277 | db: &dyn AstDatabase, | ||
278 | macro_file: MacroFile, | ||
279 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | ||
280 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | ||
281 | let _p = profile("parse_macro_query"); | ||
282 | |||
283 | let macro_call_id = macro_file.macro_call_id; | ||
284 | let (tt, err) = if let Some(arg) = arg { | ||
285 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | ||
286 | } else { | ||
287 | db.macro_expand(macro_call_id) | ||
288 | }; | ||
289 | if let Some(err) = &err { | ||
290 | // Note: | ||
291 | // The final goal we would like to make all parse_macro success, | ||
292 | // such that the following log will not call anyway. | ||
293 | match macro_call_id { | ||
294 | MacroCallId::LazyMacro(id) => { | ||
295 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | ||
296 | let node = loc.kind.node(db); | ||
297 | |||
298 | // collect parent information for warning log | ||
299 | let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { | ||
300 | it.file_id.call_node(db) | ||
301 | }) | ||
302 | .map(|n| format!("{:#}", n.value)) | ||
303 | .collect::<Vec<_>>() | ||
304 | .join("\n"); | ||
305 | |||
306 | log::warn!( | ||
307 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", | ||
308 | err, | ||
309 | node.value, | ||
310 | parents | ||
311 | ); | ||
312 | } | ||
313 | _ => { | ||
314 | log::warn!("fail on macro_parse: (reason: {})", err); | ||
315 | } | ||
316 | } | ||
317 | }; | ||
318 | let tt = tt?; | ||
319 | |||
320 | let fragment_kind = to_fragment_kind(db, macro_call_id); | ||
321 | |||
322 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | ||
323 | |||
324 | if err.is_none() { | ||
325 | Some((parse, Arc::new(rev_token_map))) | ||
326 | } else { | ||
327 | // FIXME: | ||
328 | // In future, we should propagate the actual error with recovery information | ||
329 | // instead of ignore the error here. | ||
330 | |||
331 | // Safe check for recurisve identity macro | ||
332 | let node = parse.syntax_node(); | ||
333 | let file: HirFileId = macro_file.into(); | ||
334 | let call_node = file.call_node(db)?; | ||
335 | |||
336 | if !diff(&node, &call_node.value).is_empty() { | ||
337 | Some((parse, Arc::new(rev_token_map))) | ||
338 | } else { | ||
339 | None | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | |||
344 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | ||
345 | /// FIXME: Not completed | ||
346 | fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { | ||
347 | let lazy_id = match id { | ||
348 | MacroCallId::LazyMacro(id) => id, | ||
349 | MacroCallId::EagerMacro(id) => { | ||
350 | return db.lookup_intern_eager_expansion(id).fragment; | ||
351 | } | ||
352 | }; | ||
353 | let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value; | ||
354 | |||
355 | let parent = match syn.parent() { | ||
356 | Some(it) => it, | ||
357 | None => { | ||
358 | // FIXME: | ||
359 | // If it is root, which means the parent HirFile | ||
360 | // MacroKindFile must be non-items | ||
361 | // return expr now. | ||
362 | return FragmentKind::Expr; | ||
363 | } | ||
364 | }; | ||
365 | |||
366 | match parent.kind() { | ||
367 | MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, | ||
368 | ITEM_LIST => FragmentKind::Items, | ||
369 | LET_STMT => { | ||
370 | // FIXME: Handle Pattern | ||
371 | FragmentKind::Expr | ||
372 | } | ||
373 | // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that | ||
374 | EXPR_STMT | BLOCK_EXPR => FragmentKind::Expr, | ||
375 | ARG_LIST => FragmentKind::Expr, | ||
376 | TRY_EXPR => FragmentKind::Expr, | ||
377 | TUPLE_EXPR => FragmentKind::Expr, | ||
378 | PAREN_EXPR => FragmentKind::Expr, | ||
379 | |||
380 | FOR_EXPR => FragmentKind::Expr, | ||
381 | PATH_EXPR => FragmentKind::Expr, | ||
382 | LAMBDA_EXPR => FragmentKind::Expr, | ||
383 | CONDITION => FragmentKind::Expr, | ||
384 | BREAK_EXPR => FragmentKind::Expr, | ||
385 | RETURN_EXPR => FragmentKind::Expr, | ||
386 | MATCH_EXPR => FragmentKind::Expr, | ||
387 | MATCH_ARM => FragmentKind::Expr, | ||
388 | MATCH_GUARD => FragmentKind::Expr, | ||
389 | RECORD_EXPR_FIELD => FragmentKind::Expr, | ||
390 | CALL_EXPR => FragmentKind::Expr, | ||
391 | INDEX_EXPR => FragmentKind::Expr, | ||
392 | METHOD_CALL_EXPR => FragmentKind::Expr, | ||
393 | AWAIT_EXPR => FragmentKind::Expr, | ||
394 | CAST_EXPR => FragmentKind::Expr, | ||
395 | REF_EXPR => FragmentKind::Expr, | ||
396 | PREFIX_EXPR => FragmentKind::Expr, | ||
397 | RANGE_EXPR => FragmentKind::Expr, | ||
398 | BIN_EXPR => FragmentKind::Expr, | ||
399 | _ => { | ||
400 | // Unknown , Just guess it is `Items` | ||
401 | FragmentKind::Items | ||
402 | } | ||
403 | } | ||
404 | } | ||