]>
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> | |
c0240ec0 | 21 | use base_db::CrateId; |
31ef2f64 | 22 | use mbe::DocCommentDesugarMode; |
e8be2606 | 23 | use span::SyntaxContextId; |
4b012472 | 24 | use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; |
fe692bf9 | 25 | use triomphe::Arc; |
064997fb FG |
26 | |
27 | use crate::{ | |
28 | ast::{self, AstNode}, | |
353b0b11 | 29 | db::ExpandDatabase, |
064997fb | 30 | mod_path::ModPath, |
e8be2606 | 31 | AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, Intern, |
c0240ec0 | 32 | MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, |
064997fb FG |
33 | }; |
34 | ||
fe692bf9 | 35 | pub fn expand_eager_macro_input( |
353b0b11 | 36 | db: &dyn ExpandDatabase, |
064997fb | 37 | krate: CrateId, |
e8be2606 FG |
38 | macro_call: &ast::MacroCall, |
39 | ast_id: AstId<ast::MacroCall>, | |
064997fb | 40 | def: MacroDefId, |
e8be2606 | 41 | call_site: SyntaxContextId, |
064997fb | 42 | resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, |
add651ee | 43 | ) -> ExpandResult<Option<MacroCallId>> { |
e8be2606 | 44 | let expand_to = ExpandTo::from_call_site(macro_call); |
064997fb FG |
45 | |
46 | // Note: | |
fe692bf9 FG |
47 | // When `lazy_expand` is called, its *parent* file must already exist. |
48 | // Here we store an eager macro id for the argument expanded subtree | |
064997fb | 49 | // for that purpose. |
c0240ec0 | 50 | let arg_id = MacroCallLoc { |
064997fb FG |
51 | def, |
52 | krate, | |
e8be2606 FG |
53 | kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None }, |
54 | ctxt: call_site, | |
c0240ec0 FG |
55 | } |
56 | .intern(db); | |
e8be2606 | 57 | let (_, _, span) = db.macro_arg(arg_id); |
add651ee FG |
58 | let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = |
59 | db.parse_macro_expansion(arg_id.as_macro_file()); | |
4b012472 FG |
60 | |
61 | let mut arg_map = ExpansionSpanMap::empty(); | |
add651ee FG |
62 | |
63 | let ExpandResult { value: expanded_eager_input, err } = { | |
64 | eager_macro_recur( | |
65 | db, | |
4b012472 FG |
66 | &arg_exp_map, |
67 | &mut arg_map, | |
68 | TextSize::new(0), | |
add651ee FG |
69 | InFile::new(arg_id.as_file(), arg_exp.syntax_node()), |
70 | krate, | |
4b012472 | 71 | call_site, |
add651ee FG |
72 | resolver, |
73 | ) | |
fe692bf9 | 74 | }; |
add651ee | 75 | let err = parse_err.or(err); |
4b012472 FG |
76 | if cfg!(debug_assertions) { |
77 | arg_map.finish(); | |
78 | } | |
add651ee | 79 | |
4b012472 | 80 | let Some((expanded_eager_input, _mapping)) = expanded_eager_input else { |
add651ee FG |
81 | return ExpandResult { value: None, err }; |
82 | }; | |
83 | ||
31ef2f64 FG |
84 | let mut subtree = mbe::syntax_node_to_token_tree( |
85 | &expanded_eager_input, | |
86 | arg_map, | |
87 | span, | |
88 | DocCommentDesugarMode::Mbe, | |
89 | ); | |
add651ee | 90 | |
c0240ec0 | 91 | subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; |
064997fb | 92 | |
fe692bf9 FG |
93 | let loc = MacroCallLoc { |
94 | def, | |
95 | krate, | |
e8be2606 FG |
96 | kind: MacroCallKind::FnLike { |
97 | ast_id, | |
98 | expand_to, | |
99 | eager: Some(Arc::new(EagerCallInfo { | |
100 | arg: Arc::new(subtree), | |
101 | arg_id, | |
102 | error: err.clone(), | |
103 | span, | |
104 | })), | |
105 | }, | |
106 | ctxt: call_site, | |
fe692bf9 | 107 | }; |
064997fb | 108 | |
c0240ec0 | 109 | ExpandResult { value: Some(loc.intern(db)), err } |
064997fb FG |
110 | } |
111 | ||
112 | fn lazy_expand( | |
353b0b11 | 113 | db: &dyn ExpandDatabase, |
064997fb | 114 | def: &MacroDefId, |
e8be2606 FG |
115 | macro_call: &ast::MacroCall, |
116 | ast_id: AstId<ast::MacroCall>, | |
064997fb | 117 | krate: CrateId, |
e8be2606 | 118 | call_site: SyntaxContextId, |
4b012472 | 119 | ) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> { |
e8be2606 FG |
120 | let expand_to = ExpandTo::from_call_site(macro_call); |
121 | let id = def.make_call( | |
122 | db, | |
123 | krate, | |
124 | MacroCallKind::FnLike { ast_id, expand_to, eager: None }, | |
125 | call_site, | |
126 | ); | |
fe692bf9 | 127 | let macro_file = id.as_macro_file(); |
064997fb | 128 | |
add651ee FG |
129 | db.parse_macro_expansion(macro_file) |
130 | .map(|parse| (InFile::new(macro_file.into(), parse.0), parse.1)) | |
064997fb FG |
131 | } |
132 | ||
133 | fn eager_macro_recur( | |
353b0b11 | 134 | db: &dyn ExpandDatabase, |
4b012472 FG |
135 | span_map: &ExpansionSpanMap, |
136 | expanded_map: &mut ExpansionSpanMap, | |
137 | mut offset: TextSize, | |
064997fb FG |
138 | curr: InFile<SyntaxNode>, |
139 | krate: CrateId, | |
e8be2606 | 140 | call_site: SyntaxContextId, |
064997fb | 141 | macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, |
4b012472 | 142 | ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> { |
064997fb FG |
143 | let original = curr.value.clone_for_update(); |
144 | ||
064997fb FG |
145 | let mut replacements = Vec::new(); |
146 | ||
add651ee | 147 | // FIXME: We only report a single error inside of eager expansions |
fe692bf9 | 148 | let mut error = None; |
add651ee | 149 | let mut children = original.preorder_with_tokens(); |
fe692bf9 | 150 | |
064997fb | 151 | // Collect replacement |
add651ee | 152 | while let Some(child) = children.next() { |
add651ee | 153 | let call = match child { |
4b012472 | 154 | WalkEvent::Enter(SyntaxElement::Node(child)) => match ast::MacroCall::cast(child) { |
add651ee FG |
155 | Some(it) => { |
156 | children.skip_subtree(); | |
157 | it | |
158 | } | |
4b012472 | 159 | _ => continue, |
add651ee | 160 | }, |
4b012472 FG |
161 | WalkEvent::Enter(_) => continue, |
162 | WalkEvent::Leave(child) => { | |
163 | if let SyntaxElement::Token(t) = child { | |
164 | let start = t.text_range().start(); | |
165 | offset += t.text_range().len(); | |
166 | expanded_map.push(offset, span_map.span_at(start)); | |
167 | } | |
add651ee FG |
168 | continue; |
169 | } | |
170 | }; | |
4b012472 | 171 | |
c620b35d FG |
172 | let def = match call.path().and_then(|path| { |
173 | ModPath::from_src(db, path, &mut |range| span_map.span_at(range.start()).ctx) | |
174 | }) { | |
add651ee FG |
175 | Some(path) => match macro_resolver(path.clone()) { |
176 | Some(def) => def, | |
177 | None => { | |
178 | error = | |
179 | Some(ExpandError::other(format!("unresolved macro {}", path.display(db)))); | |
4b012472 | 180 | offset += call.syntax().text_range().len(); |
add651ee FG |
181 | continue; |
182 | } | |
183 | }, | |
064997fb | 184 | None => { |
fe692bf9 | 185 | error = Some(ExpandError::other("malformed macro invocation")); |
4b012472 | 186 | offset += call.syntax().text_range().len(); |
064997fb FG |
187 | continue; |
188 | } | |
189 | }; | |
e8be2606 | 190 | let ast_id = db.ast_id_map(curr.file_id).ast_id(&call); |
fe692bf9 | 191 | let ExpandResult { value, err } = match def.kind { |
064997fb | 192 | MacroDefKind::BuiltInEager(..) => { |
add651ee | 193 | let ExpandResult { value, err } = expand_eager_macro_input( |
064997fb FG |
194 | db, |
195 | krate, | |
e8be2606 FG |
196 | &call, |
197 | curr.with_value(ast_id), | |
064997fb | 198 | def, |
4b012472 | 199 | call_site, |
064997fb | 200 | macro_resolver, |
add651ee | 201 | ); |
fe692bf9 | 202 | match value { |
add651ee | 203 | Some(call_id) => { |
4b012472 | 204 | let ExpandResult { value: (parse, map), err: err2 } = |
add651ee FG |
205 | db.parse_macro_expansion(call_id.as_macro_file()); |
206 | ||
4b012472 | 207 | map.iter().for_each(|(o, span)| expanded_map.push(o + offset, span)); |
add651ee | 208 | |
4b012472 | 209 | let syntax_node = parse.syntax_node(); |
fe692bf9 | 210 | ExpandResult { |
4b012472 FG |
211 | value: Some(( |
212 | syntax_node.clone_for_update(), | |
213 | offset + syntax_node.text_range().len(), | |
214 | )), | |
fe692bf9 FG |
215 | err: err.or(err2), |
216 | } | |
217 | } | |
218 | None => ExpandResult { value: None, err }, | |
219 | } | |
064997fb FG |
220 | } |
221 | MacroDefKind::Declarative(_) | |
222 | | MacroDefKind::BuiltIn(..) | |
223 | | MacroDefKind::BuiltInAttr(..) | |
224 | | MacroDefKind::BuiltInDerive(..) | |
225 | | MacroDefKind::ProcMacro(..) => { | |
add651ee | 226 | let ExpandResult { value: (parse, tm), err } = |
e8be2606 | 227 | lazy_expand(db, &def, &call, curr.with_value(ast_id), krate, call_site); |
064997fb FG |
228 | |
229 | // replace macro inside | |
fe692bf9 FG |
230 | let ExpandResult { value, err: error } = eager_macro_recur( |
231 | db, | |
4b012472 FG |
232 | &tm, |
233 | expanded_map, | |
234 | offset, | |
fe692bf9 | 235 | // FIXME: We discard parse errors here |
add651ee | 236 | parse.as_ref().map(|it| it.syntax_node()), |
fe692bf9 | 237 | krate, |
4b012472 | 238 | call_site, |
fe692bf9 | 239 | macro_resolver, |
add651ee | 240 | ); |
fe692bf9 | 241 | let err = err.or(error); |
add651ee | 242 | |
4b012472 | 243 | ExpandResult { value, err } |
064997fb FG |
244 | } |
245 | }; | |
fe692bf9 FG |
246 | if err.is_some() { |
247 | error = err; | |
248 | } | |
064997fb | 249 | // check if the whole original syntax is replaced |
add651ee | 250 | if call.syntax() == &original { |
4b012472 | 251 | return ExpandResult { value, err: error }; |
064997fb FG |
252 | } |
253 | ||
4b012472 FG |
254 | match value { |
255 | Some((insert, new_offset)) => { | |
256 | replacements.push((call, insert)); | |
257 | offset = new_offset; | |
258 | } | |
259 | None => offset += call.syntax().text_range().len(), | |
fe692bf9 | 260 | } |
064997fb FG |
261 | } |
262 | ||
263 | replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); | |
4b012472 | 264 | ExpandResult { value: Some((original, offset)), err: error } |
064997fb | 265 | } |