]>
Commit | Line | Data |
---|---|---|
064997fb FG |
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> | |
064997fb | 21 | use base_db::CrateId; |
fe692bf9 FG |
22 | use syntax::{ted, Parse, SyntaxNode}; |
23 | use triomphe::Arc; | |
064997fb FG |
24 | |
25 | use crate::{ | |
26 | ast::{self, AstNode}, | |
353b0b11 | 27 | db::ExpandDatabase, |
064997fb FG |
28 | hygiene::Hygiene, |
29 | mod_path::ModPath, | |
30 | EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind, | |
31 | MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro, | |
32 | }; | |
33 | ||
fe692bf9 | 34 | pub fn expand_eager_macro_input( |
353b0b11 | 35 | db: &dyn ExpandDatabase, |
064997fb FG |
36 | krate: CrateId, |
37 | macro_call: InFile<ast::MacroCall>, | |
38 | def: MacroDefId, | |
39 | resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, | |
fe692bf9 FG |
40 | ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> { |
41 | assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..))); | |
42 | let token_tree = macro_call.value.token_tree(); | |
43 | ||
44 | let Some(token_tree) = token_tree else { | |
45 | return Ok(ExpandResult { value: None, err: | |
46 | Some(ExpandError::other( | |
47 | "invalid token tree" | |
48 | )), | |
49 | }); | |
50 | }; | |
51 | let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax()); | |
064997fb FG |
52 | |
53 | let ast_map = db.ast_id_map(macro_call.file_id); | |
54 | let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); | |
55 | let expand_to = ExpandTo::from_call_site(¯o_call.value); | |
56 | ||
57 | // Note: | |
fe692bf9 FG |
58 | // When `lazy_expand` is called, its *parent* file must already exist. |
59 | // Here we store an eager macro id for the argument expanded subtree | |
064997fb FG |
60 | // for that purpose. |
61 | let arg_id = db.intern_macro_call(MacroCallLoc { | |
62 | def, | |
63 | krate, | |
fe692bf9 FG |
64 | eager: Some(Box::new(EagerCallInfo { |
65 | arg: Arc::new((parsed_args, arg_token_map)), | |
66 | arg_id: None, | |
67 | error: None, | |
68 | })), | |
064997fb FG |
69 | kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, |
70 | }); | |
fe692bf9 FG |
71 | let arg_as_expr = match db.macro_arg_text(arg_id) { |
72 | Some(it) => it, | |
73 | None => { | |
74 | return Ok(ExpandResult { | |
75 | value: None, | |
76 | err: Some(ExpandError::other("invalid token tree")), | |
77 | }) | |
78 | } | |
79 | }; | |
80 | let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur( | |
064997fb | 81 | db, |
fe692bf9 FG |
82 | &Hygiene::new(db, macro_call.file_id), |
83 | InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)), | |
064997fb FG |
84 | krate, |
85 | resolver, | |
fe692bf9 FG |
86 | )?; |
87 | let Some(expanded_eager_input) = expanded_eager_input else { | |
88 | return Ok(ExpandResult { value: None, err }) | |
064997fb | 89 | }; |
fe692bf9 FG |
90 | let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input); |
91 | subtree.delimiter = crate::tt::Delimiter::unspecified(); | |
064997fb | 92 | |
fe692bf9 FG |
93 | let loc = MacroCallLoc { |
94 | def, | |
95 | krate, | |
96 | eager: Some(Box::new(EagerCallInfo { | |
97 | arg: Arc::new((subtree, token_map)), | |
98 | arg_id: Some(arg_id), | |
99 | error: err.clone(), | |
100 | })), | |
101 | kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, | |
102 | }; | |
064997fb | 103 | |
fe692bf9 | 104 | Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err }) |
064997fb FG |
105 | } |
106 | ||
107 | fn lazy_expand( | |
353b0b11 | 108 | db: &dyn ExpandDatabase, |
064997fb FG |
109 | def: &MacroDefId, |
110 | macro_call: InFile<ast::MacroCall>, | |
111 | krate: CrateId, | |
fe692bf9 | 112 | ) -> ExpandResult<InFile<Parse<SyntaxNode>>> { |
064997fb FG |
113 | let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); |
114 | ||
115 | let expand_to = ExpandTo::from_call_site(¯o_call.value); | |
116 | let id = def.as_lazy_macro( | |
117 | db, | |
118 | krate, | |
119 | MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to }, | |
120 | ); | |
121 | ||
fe692bf9 | 122 | let macro_file = id.as_macro_file(); |
064997fb | 123 | |
fe692bf9 | 124 | db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0)) |
064997fb FG |
125 | } |
126 | ||
127 | fn eager_macro_recur( | |
353b0b11 | 128 | db: &dyn ExpandDatabase, |
064997fb FG |
129 | hygiene: &Hygiene, |
130 | curr: InFile<SyntaxNode>, | |
131 | krate: CrateId, | |
132 | macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, | |
fe692bf9 | 133 | ) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> { |
064997fb FG |
134 | let original = curr.value.clone_for_update(); |
135 | ||
136 | let children = original.descendants().filter_map(ast::MacroCall::cast); | |
137 | let mut replacements = Vec::new(); | |
138 | ||
fe692bf9 FG |
139 | // Note: We only report a single error inside of eager expansions |
140 | let mut error = None; | |
141 | ||
064997fb FG |
142 | // Collect replacement |
143 | for child in children { | |
144 | let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { | |
9c376795 | 145 | Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?, |
064997fb | 146 | None => { |
fe692bf9 | 147 | error = Some(ExpandError::other("malformed macro invocation")); |
064997fb FG |
148 | continue; |
149 | } | |
150 | }; | |
fe692bf9 | 151 | let ExpandResult { value, err } = match def.kind { |
064997fb | 152 | MacroDefKind::BuiltInEager(..) => { |
fe692bf9 | 153 | let ExpandResult { value, err } = match expand_eager_macro_input( |
064997fb FG |
154 | db, |
155 | krate, | |
156 | curr.with_value(child.clone()), | |
157 | def, | |
158 | macro_resolver, | |
064997fb | 159 | ) { |
fe692bf9 | 160 | Ok(it) => it, |
064997fb FG |
161 | Err(err) => return Err(err), |
162 | }; | |
fe692bf9 FG |
163 | match value { |
164 | Some(call) => { | |
165 | let ExpandResult { value, err: err2 } = | |
166 | db.parse_macro_expansion(call.as_macro_file()); | |
167 | ExpandResult { | |
168 | value: Some(value.0.syntax_node().clone_for_update()), | |
169 | err: err.or(err2), | |
170 | } | |
171 | } | |
172 | None => ExpandResult { value: None, err }, | |
173 | } | |
064997fb FG |
174 | } |
175 | MacroDefKind::Declarative(_) | |
176 | | MacroDefKind::BuiltIn(..) | |
177 | | MacroDefKind::BuiltInAttr(..) | |
178 | | MacroDefKind::BuiltInDerive(..) | |
179 | | MacroDefKind::ProcMacro(..) => { | |
fe692bf9 FG |
180 | let ExpandResult { value, err } = |
181 | lazy_expand(db, &def, curr.with_value(child.clone()), krate); | |
064997fb FG |
182 | |
183 | // replace macro inside | |
fe692bf9 FG |
184 | let hygiene = Hygiene::new(db, value.file_id); |
185 | let ExpandResult { value, err: error } = eager_macro_recur( | |
186 | db, | |
187 | &hygiene, | |
188 | // FIXME: We discard parse errors here | |
189 | value.map(|it| it.syntax_node()), | |
190 | krate, | |
191 | macro_resolver, | |
192 | )?; | |
193 | let err = err.or(error); | |
194 | ExpandResult { value, err } | |
064997fb FG |
195 | } |
196 | }; | |
fe692bf9 FG |
197 | if err.is_some() { |
198 | error = err; | |
199 | } | |
064997fb FG |
200 | // check if the whole original syntax is replaced |
201 | if child.syntax() == &original { | |
fe692bf9 | 202 | return Ok(ExpandResult { value, err: error }); |
064997fb FG |
203 | } |
204 | ||
fe692bf9 FG |
205 | if let Some(insert) = value { |
206 | replacements.push((child, insert)); | |
207 | } | |
064997fb FG |
208 | } |
209 | ||
210 | replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); | |
fe692bf9 | 211 | Ok(ExpandResult { value: Some(original), err: error }) |
064997fb | 212 | } |