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