1use base_db::{Crate, SourceDatabase};
22use span::SyntaxContext;
23use syntax::{
24 AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, syntax_editor::SyntaxEditor,
25};
26use syntax_bridge::DocCommentDesugarMode;
27
28use crate::{
29 AstId, EagerCallInfo, ExpandError, ExpandErrorKind, ExpandResult, ExpandTo, ExpansionSpanMap,
30 InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
31 ast::{self, AstNode},
32 mod_path::ModPath,
33};
34
35pub type EagerCallBackFn<'a> = &'a mut dyn FnMut(
36 InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
37 MacroCallId,
38);
39
40pub fn expand_eager_macro_input(
41 db: &dyn SourceDatabase,
42 krate: Crate,
43 macro_call: &ast::MacroCall,
44 ast_id: AstId<ast::MacroCall>,
45 def: MacroDefId,
46 call_site: SyntaxContext,
47 macro_depth: u32,
48 recursion_limit: u32,
49 resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
50 eager_callback: EagerCallBackFn<'_>,
51) -> ExpandResult<Option<MacroCallId>> {
52 let expand_to = ExpandTo::from_call_site(macro_call);
53
54 let loc = MacroCallLoc {
59 def,
60 krate,
61 kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
62 ctxt: call_site,
63 macro_depth,
64 };
65 let arg_id = MacroCallId::new(db, loc);
66 #[expect(deprecated, reason = "builtin eager macros are never derives")]
67 let (_, _, span) = arg_id.macro_arg(db);
68 let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
69 arg_id.parse_macro_expansion(db);
70
71 let mut arg_map = ExpansionSpanMap::empty();
72
73 let ExpandResult { value: expanded_eager_input, err } = {
74 eager_macro_recur(
75 db,
76 arg_exp_map,
77 &mut arg_map,
78 TextSize::new(0),
79 InFile::new(arg_id.into(), arg_exp.syntax_node()),
80 krate,
81 call_site,
82 macro_depth,
83 recursion_limit,
84 resolver,
85 eager_callback,
86 )
87 };
88 let err = parse_err.clone().or(err);
89 if cfg!(debug_assertions) {
90 arg_map.finish();
91 }
92
93 let Some((expanded_eager_input, _mapping)) = expanded_eager_input else {
94 return ExpandResult { value: None, err };
95 };
96
97 let mut subtree = syntax_bridge::syntax_node_to_token_tree(
98 &expanded_eager_input,
99 arg_map,
100 *span,
101 DocCommentDesugarMode::Mbe,
102 );
103
104 subtree.set_top_subtree_delimiter_kind(crate::tt::DelimiterKind::Invisible);
105
106 let loc = MacroCallLoc {
107 def,
108 krate,
109 kind: MacroCallKind::FnLike {
110 ast_id,
111 expand_to,
112 eager: Some(Box::new(EagerCallInfo {
113 arg: subtree,
114 arg_id,
115 error: err.clone(),
116 span: *span,
117 })),
118 },
119 ctxt: call_site,
120 macro_depth,
121 };
122
123 ExpandResult { value: Some(MacroCallId::new(db, loc)), err }
124}
125
126fn lazy_expand<'db>(
127 db: &'db dyn SourceDatabase,
128 def: &MacroDefId,
129 macro_call: &ast::MacroCall,
130 ast_id: AstId<ast::MacroCall>,
131 krate: Crate,
132 call_site: SyntaxContext,
133 macro_depth: u32,
134 eager_callback: EagerCallBackFn<'_>,
135) -> ExpandResult<(InFile<Parse<SyntaxNode>>, &'db ExpansionSpanMap)> {
136 let expand_to = ExpandTo::from_call_site(macro_call);
137 let id = def.make_call(
138 db,
139 krate,
140 MacroCallKind::FnLike { ast_id, expand_to, eager: None },
141 call_site,
142 macro_depth,
143 );
144 eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id);
145
146 id.parse_macro_expansion(db)
147 .as_ref()
148 .map(|parse| (InFile::new(id.into(), parse.0.clone()), &parse.1))
149}
150
151fn eager_macro_recur(
152 db: &dyn SourceDatabase,
153 span_map: &ExpansionSpanMap,
154 expanded_map: &mut ExpansionSpanMap,
155 mut offset: TextSize,
156 curr: InFile<SyntaxNode>,
157 krate: Crate,
158 call_site: SyntaxContext,
159 macro_depth: u32,
160 recursion_limit: u32,
161 macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
162 eager_callback: EagerCallBackFn<'_>,
163) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
164 let (editor, _) = SyntaxEditor::new(curr.value.clone());
165 let original = curr.value.clone();
166
167 let mut replacements = Vec::new();
168
169 let mut error = None;
171 let mut children = original.preorder_with_tokens();
172
173 while let Some(child) = children.next() {
175 let call = match child {
176 WalkEvent::Enter(SyntaxElement::Node(child)) => match ast::MacroCall::cast(child) {
177 Some(it) => {
178 children.skip_subtree();
179 it
180 }
181 _ => continue,
182 },
183 WalkEvent::Enter(_) => continue,
184 WalkEvent::Leave(child) => {
185 if let SyntaxElement::Token(t) = child {
186 let start = t.text_range().start();
187 offset += t.text_range().len();
188 expanded_map.push(offset, span_map.span_at(start));
189 }
190 continue;
191 }
192 };
193
194 let def = match call.path().and_then(|path| {
195 ModPath::from_src(db, path, &mut |range| span_map.span_at(range.start()).ctx)
196 }) {
197 Some(path) => match macro_resolver(&path) {
198 Some(def) => def,
199 None => {
200 let edition = krate.data(db).edition;
201 error = Some(ExpandError::other(
202 span_map.span_at(call.syntax().text_range().start()),
203 format!("unresolved macro {}", path.display(db, edition)),
204 ));
205 offset += call.syntax().text_range().len();
206 continue;
207 }
208 },
209 None => {
210 error = Some(ExpandError::other(
211 span_map.span_at(call.syntax().text_range().start()),
212 "malformed macro invocation",
213 ));
214 offset += call.syntax().text_range().len();
215 continue;
216 }
217 };
218 let ast_id = curr.file_id.ast_id_map(db).ast_id(&call);
219
220 if macro_depth > recursion_limit {
221 return ExpandResult::only_err(ExpandError::new(
222 span_map.span_at(call.syntax().text_range().start()),
223 ExpandErrorKind::RecursionOverflow,
224 ));
225 }
226
227 let ExpandResult { value, err } = match def.kind {
228 MacroDefKind::BuiltInEager(..) => {
229 let ExpandResult { value, err } = expand_eager_macro_input(
230 db,
231 krate,
232 &call,
233 curr.with_value(ast_id),
234 def,
235 call_site,
236 macro_depth + 1,
237 recursion_limit,
238 macro_resolver,
239 eager_callback,
240 );
241 match value {
242 Some(call_id) => {
243 eager_callback(
244 curr.with_value(ast_id).map(|ast_id| (AstPtr::new(&call), ast_id)),
245 call_id,
246 );
247 let ExpandResult { value: (parse, map), err: err2 } =
248 call_id.parse_macro_expansion(db);
249
250 map.iter().for_each(|(o, span)| expanded_map.push(o + offset, span));
251
252 let syntax_node = parse.syntax_node();
253 ExpandResult {
254 value: Some((
255 syntax_node.clone(),
256 offset + syntax_node.text_range().len(),
257 )),
258 err: err.clone().or_else(|| err2.clone()),
259 }
260 }
261 None => ExpandResult { value: None, err },
262 }
263 }
264 MacroDefKind::Declarative(..)
265 | MacroDefKind::BuiltIn(..)
266 | MacroDefKind::BuiltInAttr(..)
267 | MacroDefKind::BuiltInDerive(..)
268 | MacroDefKind::ProcMacro(..)
269 | MacroDefKind::UnimplementedBuiltIn(..) => {
270 let ExpandResult { value: (parse, tm), err } = lazy_expand(
271 db,
272 &def,
273 &call,
274 curr.with_value(ast_id),
275 krate,
276 call_site,
277 macro_depth,
278 eager_callback,
279 );
280
281 let ExpandResult { value, err: error } = eager_macro_recur(
283 db,
284 tm,
285 expanded_map,
286 offset,
287 parse.as_ref().map(|it| it.syntax_node()),
289 krate,
290 call_site,
291 macro_depth + 1,
292 recursion_limit,
293 macro_resolver,
294 eager_callback,
295 );
296 let err = err.or(error);
297
298 ExpandResult { value, err }
299 }
300 };
301 if err.is_some() {
302 error = err;
303 }
304 if call.syntax() == &original {
306 return ExpandResult { value, err: error };
307 }
308
309 match value {
310 Some((insert, new_offset)) => {
311 replacements.push((call, insert));
312 offset = new_offset;
313 }
314 None => offset += call.syntax().text_range().len(),
315 }
316 }
317
318 replacements.into_iter().rev().for_each(|(old, new)| editor.replace(old.syntax(), new));
319 let original = editor.finish().new_root().clone();
320 ExpandResult { value: Some((original, offset)), err: error }
321}