diff options
Diffstat (limited to 'crates/ra_hir_expand/src/eager.rs')
-rw-r--r-- | crates/ra_hir_expand/src/eager.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs new file mode 100644 index 000000000..8caf8497e --- /dev/null +++ b/crates/ra_hir_expand/src/eager.rs | |||
@@ -0,0 +1,93 @@ | |||
1 | //! Eager expansion related utils | ||
2 | |||
3 | use crate::{ | ||
4 | ast::{self, AstNode}, | ||
5 | db::AstDatabase, | ||
6 | EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | ||
7 | }; | ||
8 | |||
9 | use ra_parser::FragmentKind; | ||
10 | use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; | ||
11 | use std::{collections::HashMap, sync::Arc}; | ||
12 | |||
13 | fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> { | ||
14 | let mut subtree = mbe::syntax_node_to_token_tree(node)?.0; | ||
15 | subtree.delimiter = None; | ||
16 | Some(subtree) | ||
17 | } | ||
18 | |||
19 | fn lazy_expand( | ||
20 | db: &impl AstDatabase, | ||
21 | def: &MacroDefId, | ||
22 | macro_call: InFile<ast::MacroCall>, | ||
23 | ) -> Option<InFile<SyntaxNode>> { | ||
24 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | ||
25 | |||
26 | let id: MacroCallId = | ||
27 | def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); | ||
28 | |||
29 | db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) | ||
30 | } | ||
31 | |||
32 | fn eager_macro_recur( | ||
33 | db: &impl AstDatabase, | ||
34 | curr: InFile<SyntaxNode>, | ||
35 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | ||
36 | ) -> Option<SyntaxNode> { | ||
37 | let mut original = curr.value.clone(); | ||
38 | |||
39 | let children = curr.value.descendants().filter_map(ast::MacroCall::cast); | ||
40 | let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default(); | ||
41 | |||
42 | // Collect replacement | ||
43 | for child in children { | ||
44 | let def: MacroDefId = macro_resolver(child.path()?)?; | ||
45 | let insert = match def.kind { | ||
46 | MacroDefKind::BuiltInEager(_) => { | ||
47 | let id: MacroCallId = | ||
48 | expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? | ||
49 | .into(); | ||
50 | db.parse_or_expand(id.as_file())? | ||
51 | } | ||
52 | MacroDefKind::Declarative | ||
53 | | MacroDefKind::BuiltIn(_) | ||
54 | | MacroDefKind::BuiltInDerive(_) => { | ||
55 | let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; | ||
56 | // replace macro inside | ||
57 | eager_macro_recur(db, expanded, macro_resolver)? | ||
58 | } | ||
59 | }; | ||
60 | |||
61 | replaces.insert(child.syntax().clone().into(), insert.into()); | ||
62 | } | ||
63 | |||
64 | if !replaces.is_empty() { | ||
65 | original = replace_descendants(&original, |n| replaces.get(n).cloned()); | ||
66 | } | ||
67 | |||
68 | Some(original) | ||
69 | } | ||
70 | |||
71 | pub fn expand_eager_macro( | ||
72 | db: &impl AstDatabase, | ||
73 | macro_call: InFile<ast::MacroCall>, | ||
74 | def: MacroDefId, | ||
75 | resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | ||
76 | ) -> Option<EagerMacroId> { | ||
77 | let args = macro_call.value.token_tree()?; | ||
78 | let parsed_args = mbe::ast_to_token_tree(&args)?.0; | ||
79 | let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; | ||
80 | let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?; | ||
81 | |||
82 | let subtree = to_subtree(&result)?; | ||
83 | |||
84 | if let MacroDefKind::BuiltInEager(eager) = def.kind { | ||
85 | let (subtree, fragment) = eager.expand(&subtree).ok()?; | ||
86 | let eager = | ||
87 | EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; | ||
88 | |||
89 | Some(db.intern_eager_expansion(eager)) | ||
90 | } else { | ||
91 | None | ||
92 | } | ||
93 | } | ||