aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_expand/src/db.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_expand/src/db.rs')
-rw-r--r--crates/ra_hir_expand/src/db.rs404
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
3use std::sync::Arc;
4
5use mbe::{ExpandResult, MacroRules};
6use ra_db::{salsa, SourceDatabase};
7use ra_parser::FragmentKind;
8use ra_prof::profile;
9use ra_syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
10
11use 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)]
18pub enum TokenExpander {
19 MacroRules(mbe::MacroRules),
20 Builtin(BuiltinFnLikeExpander),
21 BuiltinDerive(BuiltinDeriveExpander),
22 ProcMacro(ProcMacroExpander),
23}
24
25impl 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)]
67pub 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.
93pub 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
113pub(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
119pub(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
153pub(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
166pub(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
175pub(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
182fn 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
195fn 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, &macro_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
233pub(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, &macro_arg.0)
258}
259
260pub(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
269pub(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
276pub 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
346fn 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}