diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-03 17:30:08 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-03 17:30:08 +0000 |
commit | 7a322f9afff05b88507a6956a2d84a3abef0a0d6 (patch) | |
tree | f811a7f405edca2b7e0c32666604117ef6486229 /crates/ra_hir_expand/src/eager.rs | |
parent | 13b25d73b56ede36d1680efc19f5c11b0669b96c (diff) | |
parent | 4d5e80c6c86aa6bfee50e9f8b80b365c3120ed80 (diff) |
Merge #3392
3392: Implement concat eager macro r=matklad a=edwin0cheng
This PR implements the following things:
1. Add basic eager macro infrastructure by introducing `EagerCallId` such that the new `MacroCallId` is defined as :
```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MacroCallId {
LazyMacro(LazyMacroId),
EagerMacro(EagerMacroId),
}
```
2. Add `concat!` builtin macro.
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_hir_expand/src/eager.rs')
-rw-r--r-- | crates/ra_hir_expand/src/eager.rs | 112 |
1 files changed, 112 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..7fcdfab5a --- /dev/null +++ b/crates/ra_hir_expand/src/eager.rs | |||
@@ -0,0 +1,112 @@ | |||
1 | //! Eager expansion related utils | ||
2 | //! | ||
3 | //! Here is a dump of a discussion from Vadim Petrochenkov about Eager Expansion and | ||
4 | //! Its name resolution : | ||
5 | //! | ||
6 | //! > Eagerly expanded macros (and also macros eagerly expanded by eagerly expanded macros, | ||
7 | //! > which actually happens in practice too!) are resolved at the location of the "root" macro | ||
8 | //! > that performs the eager expansion on its arguments. | ||
9 | //! > If some name cannot be resolved at the eager expansion time it's considered unresolved, | ||
10 | //! > even if becomes available later (e.g. from a glob import or other macro). | ||
11 | //! | ||
12 | //! > Eagerly expanded macros don't add anything to the module structure of the crate and | ||
13 | //! > don't build any speculative module structures, i.e. they are expanded in a "flat" | ||
14 | //! > way even if tokens in them look like modules. | ||
15 | //! | ||
16 | //! > In other words, it kinda works for simple cases for which it was originally intended, | ||
17 | //! > and we need to live with it because it's available on stable and widely relied upon. | ||
18 | //! | ||
19 | //! | ||
20 | //! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros | ||
21 | |||
22 | use crate::{ | ||
23 | ast::{self, AstNode}, | ||
24 | db::AstDatabase, | ||
25 | EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | ||
26 | }; | ||
27 | |||
28 | use ra_parser::FragmentKind; | ||
29 | use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; | ||
30 | use std::{collections::HashMap, sync::Arc}; | ||
31 | |||
32 | pub fn expand_eager_macro( | ||
33 | db: &impl AstDatabase, | ||
34 | macro_call: InFile<ast::MacroCall>, | ||
35 | def: MacroDefId, | ||
36 | resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | ||
37 | ) -> Option<EagerMacroId> { | ||
38 | let args = macro_call.value.token_tree()?; | ||
39 | let parsed_args = mbe::ast_to_token_tree(&args)?.0; | ||
40 | let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; | ||
41 | let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?; | ||
42 | |||
43 | let subtree = to_subtree(&result)?; | ||
44 | |||
45 | if let MacroDefKind::BuiltInEager(eager) = def.kind { | ||
46 | let (subtree, fragment) = eager.expand(&subtree).ok()?; | ||
47 | let eager = | ||
48 | EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; | ||
49 | |||
50 | Some(db.intern_eager_expansion(eager)) | ||
51 | } else { | ||
52 | None | ||
53 | } | ||
54 | } | ||
55 | |||
56 | fn to_subtree(node: &SyntaxNode) -> Option<tt::Subtree> { | ||
57 | let mut subtree = mbe::syntax_node_to_token_tree(node)?.0; | ||
58 | subtree.delimiter = None; | ||
59 | Some(subtree) | ||
60 | } | ||
61 | |||
62 | fn lazy_expand( | ||
63 | db: &impl AstDatabase, | ||
64 | def: &MacroDefId, | ||
65 | macro_call: InFile<ast::MacroCall>, | ||
66 | ) -> Option<InFile<SyntaxNode>> { | ||
67 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); | ||
68 | |||
69 | let id: MacroCallId = | ||
70 | def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); | ||
71 | |||
72 | db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) | ||
73 | } | ||
74 | |||
75 | fn eager_macro_recur( | ||
76 | db: &impl AstDatabase, | ||
77 | curr: InFile<SyntaxNode>, | ||
78 | macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, | ||
79 | ) -> Option<SyntaxNode> { | ||
80 | let mut original = curr.value.clone(); | ||
81 | |||
82 | let children = curr.value.descendants().filter_map(ast::MacroCall::cast); | ||
83 | let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default(); | ||
84 | |||
85 | // Collect replacement | ||
86 | for child in children { | ||
87 | let def: MacroDefId = macro_resolver(child.path()?)?; | ||
88 | let insert = match def.kind { | ||
89 | MacroDefKind::BuiltInEager(_) => { | ||
90 | let id: MacroCallId = | ||
91 | expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? | ||
92 | .into(); | ||
93 | db.parse_or_expand(id.as_file())? | ||
94 | } | ||
95 | MacroDefKind::Declarative | ||
96 | | MacroDefKind::BuiltIn(_) | ||
97 | | MacroDefKind::BuiltInDerive(_) => { | ||
98 | let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; | ||
99 | // replace macro inside | ||
100 | eager_macro_recur(db, expanded, macro_resolver)? | ||
101 | } | ||
102 | }; | ||
103 | |||
104 | replaces.insert(child.syntax().clone().into(), insert.into()); | ||
105 | } | ||
106 | |||
107 | if !replaces.is_empty() { | ||
108 | original = replace_descendants(&original, |n| replaces.get(n).cloned()); | ||
109 | } | ||
110 | |||
111 | Some(original) | ||
112 | } | ||