1use base_db::{Crate, RootQueryDb};
4use either::Either;
5use mbe::MatchedArmIndex;
6use rustc_hash::FxHashSet;
7use span::{AstIdMap, Edition, Span, SyntaxContext};
8use syntax::{AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
9use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
10use triomphe::Arc;
11
12use crate::{
13 AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
14 EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
15 MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
16 attrs::{AttrId, AttrInput, RawAttrs, collect_attrs},
17 builtin::pseudo_derive_attr_expansion,
18 cfg_process,
19 declarative::DeclarativeMacroExpander,
20 fixup::{self, SyntaxFixupUndoInfo},
21 hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
22 proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
23 span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
24 tt,
25};
26type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span);
28const TOKEN_LIMIT: usize = 2_097_152;
35
36#[derive(Debug, Clone, Eq, PartialEq)]
37pub enum TokenExpander {
38 DeclarativeMacro(Arc<DeclarativeMacroExpander>),
40 BuiltIn(BuiltinFnLikeExpander),
42 BuiltInEager(EagerExpander),
44 BuiltInAttr(BuiltinAttrExpander),
46 BuiltInDerive(BuiltinDeriveExpander),
48 ProcMacro(CustomProcMacroExpander),
50}
51
52#[query_group::query_group]
53pub trait ExpandDatabase: RootQueryDb {
54 #[salsa::input]
56 fn proc_macros(&self) -> Arc<ProcMacros>;
57
58 #[salsa::invoke(crate::proc_macro::proc_macros_for_crate)]
60 fn proc_macros_for_crate(&self, krate: Crate) -> Option<Arc<CrateProcMacros>>;
61
62 #[salsa::invoke(ast_id_map)]
63 #[salsa::lru(1024)]
64 fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
65
66 #[salsa::transparent]
67 fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
68
69 #[salsa::lru(512)]
71 fn parse_macro_expansion(
72 &self,
73 macro_file: MacroCallId,
74 ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
75
76 #[salsa::transparent]
77 #[salsa::invoke(SpanMap::new)]
78 fn span_map(&self, file_id: HirFileId) -> SpanMap;
79
80 #[salsa::transparent]
81 #[salsa::invoke(crate::span_map::expansion_span_map)]
82 fn expansion_span_map(&self, file_id: MacroCallId) -> Arc<ExpansionSpanMap>;
83 #[salsa::invoke(crate::span_map::real_span_map)]
84 fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>;
85
86 #[salsa::transparent]
92 fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId;
93 #[salsa::transparent]
94 fn lookup_intern_macro_call(&self, macro_call: MacroCallId) -> MacroCallLoc;
95
96 #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
100 #[salsa::invoke(macro_arg)]
101 fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
102
103 #[salsa::transparent]
104 fn macro_arg_considering_derives(
105 &self,
106 id: MacroCallId,
107 kind: &MacroCallKind,
108 ) -> MacroArgResult;
109
110 #[salsa::transparent]
112 #[salsa::invoke(TokenExpander::macro_expander)]
113 fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
114
115 #[salsa::invoke(DeclarativeMacroExpander::expander)]
117 fn decl_macro_expander(
118 &self,
119 def_crate: Crate,
120 id: AstId<ast::Macro>,
121 ) -> Arc<DeclarativeMacroExpander>;
122
123 #[salsa::invoke(expand_proc_macro)]
128 fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::TopSubtree>>;
129 #[salsa::invoke_interned(proc_macro_span)]
135 fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span;
136
137 #[salsa::invoke(parse_macro_expansion_error)]
139 fn parse_macro_expansion_error(
140 &self,
141 macro_call: MacroCallId,
142 ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
143
144 #[salsa::transparent]
145 fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext;
146}
147
148#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext, revisions = usize::MAX)]
149pub struct SyntaxContextWrapper {
150 pub data: SyntaxContext,
151}
152
153fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContext {
154 match file {
155 HirFileId::FileId(_) => SyntaxContext::root(edition),
156 HirFileId::MacroFile(m) => {
157 let kind = db.lookup_intern_macro_call(m).kind;
158 db.macro_arg_considering_derives(m, &kind).2.ctx
159 }
160 }
161}
162
163pub fn expand_speculative(
168 db: &dyn ExpandDatabase,
169 actual_macro_call: MacroCallId,
170 speculative_args: &SyntaxNode,
171 token_to_map: SyntaxToken,
172) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
173 let loc = db.lookup_intern_macro_call(actual_macro_call);
174 let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
175
176 let span_map = RealSpanMap::absolute(span.anchor.file_id);
177 let span_map = SpanMapRef::RealSpanMap(&span_map);
178
179 let (mut tt, undo_info) = match loc.kind {
181 MacroCallKind::FnLike { .. } => (
182 syntax_bridge::syntax_node_to_token_tree(
183 speculative_args,
184 span_map,
185 span,
186 if loc.def.is_proc_macro() {
187 DocCommentDesugarMode::ProcMacro
188 } else {
189 DocCommentDesugarMode::Mbe
190 },
191 ),
192 SyntaxFixupUndoInfo::NONE,
193 ),
194 MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
195 syntax_bridge::syntax_node_to_token_tree(
196 speculative_args,
197 span_map,
198 span,
199 DocCommentDesugarMode::ProcMacro,
200 ),
201 SyntaxFixupUndoInfo::NONE,
202 ),
203 MacroCallKind::Derive { derive_attr_index: index, .. }
204 | MacroCallKind::Attr { invoc_attr_index: index, .. } => {
205 let censor = if let MacroCallKind::Derive { .. } = loc.kind {
206 censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
207 } else {
208 attr_source(index, &ast::Item::cast(speculative_args.clone())?)
209 .into_iter()
210 .map(|it| it.syntax().clone().into())
211 .collect()
212 };
213
214 let censor_cfg =
215 cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default();
216 let mut fixups = fixup::fixup_syntax(
217 span_map,
218 speculative_args,
219 span,
220 DocCommentDesugarMode::ProcMacro,
221 );
222 fixups.append.retain(|it, _| match it {
223 syntax::NodeOrToken::Token(_) => true,
224 it => !censor.contains(it) && !censor_cfg.contains(it),
225 });
226 fixups.remove.extend(censor);
227 fixups.remove.extend(censor_cfg);
228
229 (
230 syntax_bridge::syntax_node_to_token_tree_modified(
231 speculative_args,
232 span_map,
233 fixups.append,
234 fixups.remove,
235 span,
236 DocCommentDesugarMode::ProcMacro,
237 ),
238 fixups.undo_info,
239 )
240 }
241 };
242
243 let attr_arg = match loc.kind {
244 MacroCallKind::Attr { invoc_attr_index, .. } => {
245 if loc.def.is_attribute_derive() {
246 ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
248 |token_tree| {
249 let mut tree = syntax_node_to_token_tree(
250 token_tree.syntax(),
251 span_map,
252 span,
253 DocCommentDesugarMode::ProcMacro,
254 );
255 *tree.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
256 tree
257 },
258 )
259 } else {
260 let item = ast::Item::cast(speculative_args.clone())?;
263 let attrs = RawAttrs::new_expanded(db, &item, span_map, loc.krate.cfg_options(db));
264 attrs.iter().find(|attr| attr.id == invoc_attr_index).and_then(|attr| {
265 match attr.input.as_deref()? {
266 AttrInput::TokenTree(tt) => {
267 let mut attr_arg = tt.clone();
268 attr_arg.top_subtree_delimiter_mut().kind =
269 tt::DelimiterKind::Invisible;
270 Some(attr_arg)
271 }
272 AttrInput::Literal(_) => None,
273 }
274 })
275 }
276 }
277 _ => None,
278 };
279
280 let mut speculative_expansion = match loc.def.kind {
283 MacroDefKind::ProcMacro(ast, expander, _) => {
284 let span = db.proc_macro_span(ast);
285 *tt.top_subtree_delimiter_mut() = tt::Delimiter::invisible_spanned(span);
286 expander.expand(
287 db,
288 loc.def.krate,
289 loc.krate,
290 &tt,
291 attr_arg.as_ref(),
292 span_with_def_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
293 span_with_call_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
294 span_with_mixed_site_ctxt(db, span, actual_macro_call.into(), loc.def.edition),
295 )
296 }
297 MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
298 pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
299 }
300 MacroDefKind::Declarative(it) => {
301 db.decl_macro_expander(loc.krate, it).expand_unhygienic(tt, span, loc.def.edition)
302 }
303 MacroDefKind::BuiltIn(_, it) => {
304 it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
305 }
306 MacroDefKind::BuiltInDerive(_, it) => {
307 it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
308 }
309 MacroDefKind::BuiltInEager(_, it) => {
310 it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
311 }
312 MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span),
313 };
314
315 let expand_to = loc.expand_to();
316
317 fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
318 let (node, rev_tmap) =
319 token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to, loc.def.edition);
320
321 let syntax_node = node.syntax_node();
322 let token = rev_tmap
323 .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
324 .filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx)))
325 .map(|(t, ctx)| {
326 let ranking = ctx.is_opaque(db) as u8
330 + 2 * (t.kind() != token_to_map.kind()) as u8
331 + 4 * ((t.text() != token_to_map.text()) as u8);
332 (t, ranking)
333 })
334 .collect();
335 Some((node.syntax_node(), token))
336}
337
338fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> triomphe::Arc<AstIdMap> {
339 triomphe::Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
340}
341
342fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
345 match file_id {
346 HirFileId::FileId(file_id) => db.parse(file_id).syntax_node(),
347 HirFileId::MacroFile(macro_file) => {
348 db.parse_macro_expansion(macro_file).value.0.syntax_node()
349 }
350 }
351}
352
353fn parse_macro_expansion(
356 db: &dyn ExpandDatabase,
357 macro_file: MacroCallId,
358) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
359 let _p = tracing::info_span!("parse_macro_expansion").entered();
360 let loc = db.lookup_intern_macro_call(macro_file);
361 let def_edition = loc.def.edition;
362 let expand_to = loc.expand_to();
363 let mbe::ValueResult { value: (tt, matched_arm), err } = macro_expand(db, macro_file, loc);
364
365 let (parse, mut rev_token_map) = token_tree_to_syntax_node(
366 db,
367 match &tt {
368 CowArc::Arc(it) => it,
369 CowArc::Owned(it) => it,
370 },
371 expand_to,
372 def_edition,
373 );
374 rev_token_map.matched_arm = matched_arm;
375
376 ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
377}
378
379fn parse_macro_expansion_error(
380 db: &dyn ExpandDatabase,
381 macro_call_id: MacroCallId,
382) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> {
383 let e: ExpandResult<Arc<[SyntaxError]>> =
384 db.parse_macro_expansion(macro_call_id).map(|it| Arc::from(it.0.errors()));
385 if e.value.is_empty() && e.err.is_none() { None } else { Some(Arc::new(e)) }
386}
387
388pub(crate) fn parse_with_map(
389 db: &dyn ExpandDatabase,
390 file_id: HirFileId,
391) -> (Parse<SyntaxNode>, SpanMap) {
392 match file_id {
393 HirFileId::FileId(file_id) => {
394 (db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
395 }
396 HirFileId::MacroFile(macro_file) => {
397 let (parse, map) = db.parse_macro_expansion(macro_file).value;
398 (parse, SpanMap::ExpansionSpanMap(map))
399 }
400 }
401}
402
403#[allow(deprecated)] fn macro_arg_considering_derives(
409 db: &dyn ExpandDatabase,
410 id: MacroCallId,
411 kind: &MacroCallKind,
412) -> MacroArgResult {
413 match kind {
414 MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id),
416 _ => db.macro_arg(id),
418 }
419}
420
421fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
422 let loc = db.lookup_intern_macro_call(id);
423
424 if let MacroCallLoc {
425 def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
426 kind: MacroCallKind::FnLike { eager: Some(eager), .. },
427 ..
428 } = &loc
429 {
430 return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span);
431 }
432
433 let (parse, map) = parse_with_map(db, loc.kind.file_id());
434 let root = parse.syntax_node();
435
436 let (censor, item_node, span) = match loc.kind {
437 MacroCallKind::FnLike { ast_id, .. } => {
438 let node = &ast_id.to_ptr(db).to_node(&root);
439 let path_range = node
440 .path()
441 .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range());
442 let span = map.span_for_range(path_range);
443
444 let dummy_tt = |kind| {
445 (
446 Arc::new(tt::TopSubtree::from_token_trees(
447 tt::Delimiter { open: span, close: span, kind },
448 tt::TokenTreesView::new(&[]),
449 )),
450 SyntaxFixupUndoInfo::default(),
451 span,
452 )
453 };
454
455 let Some(tt) = node.token_tree() else {
456 return dummy_tt(tt::DelimiterKind::Invisible);
457 };
458 let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
459 let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
460
461 let mismatched_delimiters = !matches!(
462 (first, last),
463 (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
464 );
465 if mismatched_delimiters {
466 cov_mark::hit!(issue9358_bad_macro_stack_overflow);
473
474 let kind = match first {
475 _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
476 T!['('] => tt::DelimiterKind::Parenthesis,
477 T!['['] => tt::DelimiterKind::Bracket,
478 T!['{'] => tt::DelimiterKind::Brace,
479 _ => tt::DelimiterKind::Invisible,
480 };
481 return dummy_tt(kind);
482 }
483
484 let mut tt = syntax_bridge::syntax_node_to_token_tree(
485 tt.syntax(),
486 map.as_ref(),
487 span,
488 if loc.def.is_proc_macro() {
489 DocCommentDesugarMode::ProcMacro
490 } else {
491 DocCommentDesugarMode::Mbe
492 },
493 );
494 if loc.def.is_proc_macro() {
495 tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
497 }
498 return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
499 }
500 MacroCallKind::Derive { .. } => {
502 unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`")
503 }
504 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
505 let node = ast_id.to_ptr(db).to_node(&root);
506 let attr_source = attr_source(invoc_attr_index, &node);
507
508 let span = map.span_for_range(
509 attr_source
510 .as_ref()
511 .and_then(|it| it.path())
512 .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
513 );
514 if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
516 && ast::Adt::can_cast(node.syntax().kind())
517 {
518 let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
519 let censor_derive_input = censor_derive_input(invoc_attr_index, &adt);
520 (censor_derive_input, node, span)
521 } else {
522 (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
523 }
524 }
525 };
526
527 let (mut tt, undo_info) = {
528 let syntax = item_node.syntax();
529 let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default();
530 let mut fixups =
531 fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro);
532 fixups.append.retain(|it, _| match it {
533 syntax::NodeOrToken::Token(_) => true,
534 it => !censor.contains(it) && !censor_cfg.contains(it),
535 });
536 fixups.remove.extend(censor);
537 fixups.remove.extend(censor_cfg);
538
539 (
540 syntax_bridge::syntax_node_to_token_tree_modified(
541 syntax,
542 map,
543 fixups.append,
544 fixups.remove,
545 span,
546 DocCommentDesugarMode::ProcMacro,
547 ),
548 fixups.undo_info,
549 )
550 };
551
552 if loc.def.is_proc_macro() {
553 tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
555 }
556
557 (Arc::new(tt), undo_info, span)
558}
559
560fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<SyntaxElement> {
563 cov_mark::hit!(derive_censoring);
565 collect_attrs(node)
566 .take(derive_attr_index.ast_index() + 1)
567 .filter_map(|(_, attr)| Either::left(attr))
568 .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
574 .map(|it| it.syntax().clone().into())
575 .collect()
576}
577
578fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option<ast::Attr> {
580 cov_mark::hit!(attribute_macro_attr_censoring);
582 collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr))
583}
584
585impl TokenExpander {
586 fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
587 match id.kind {
588 MacroDefKind::Declarative(ast_id) => {
589 TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
590 }
591 MacroDefKind::BuiltIn(_, expander) => TokenExpander::BuiltIn(expander),
592 MacroDefKind::BuiltInAttr(_, expander) => TokenExpander::BuiltInAttr(expander),
593 MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander),
594 MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander),
595 MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander),
596 }
597 }
598}
599
600enum CowArc<T> {
601 Arc(Arc<T>),
602 Owned(T),
603}
604
605fn macro_expand(
606 db: &dyn ExpandDatabase,
607 macro_call_id: MacroCallId,
608 loc: MacroCallLoc,
609) -> ExpandResult<(CowArc<tt::TopSubtree>, MatchedArmIndex)> {
610 let _p = tracing::info_span!("macro_expand").entered();
611
612 let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
613 MacroDefKind::ProcMacro(..) => {
614 return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None);
615 }
616 _ => {
617 let (macro_arg, undo_info, span) =
618 db.macro_arg_considering_derives(macro_call_id, &loc.kind);
619
620 let arg = &*macro_arg;
621 let res =
622 match loc.def.kind {
623 MacroDefKind::Declarative(id) => db
624 .decl_macro_expander(loc.def.krate, id)
625 .expand(db, arg.clone(), macro_call_id, span),
626 MacroDefKind::BuiltIn(_, it) => {
627 it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
628 }
629 MacroDefKind::BuiltInDerive(_, it) => {
630 it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
631 }
632 MacroDefKind::BuiltInEager(_, it) => {
633 let eager = match &loc.kind {
640 MacroCallKind::FnLike { eager: None, .. } => {
641 return ExpandResult::ok(CowArc::Arc(macro_arg.clone()))
642 .zip_val(None);
643 }
644 MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
645 _ => None,
646 };
647
648 let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into);
649
650 if let Some(EagerCallInfo { error, .. }) = eager {
651 res.err = error.clone().or(res.err);
653 }
654 res.zip_val(None)
655 }
656 MacroDefKind::BuiltInAttr(_, it) => {
657 let mut res = it.expand(db, macro_call_id, arg, span);
658 fixup::reverse_fixups(&mut res.value, &undo_info);
659 res.zip_val(None)
660 }
661 MacroDefKind::ProcMacro(_, _, _) => unreachable!(),
662 };
663 (ExpandResult { value: res.value, err: res.err }, span)
664 }
665 };
666
667 if !loc.def.is_include() {
669 if let Err(value) = check_tt_count(&tt) {
671 return value
672 .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))))
673 .zip_val(matched_arm);
674 }
675 }
676
677 ExpandResult { value: (CowArc::Owned(tt), matched_arm), err }
678}
679
680fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
681 let root = db.parse_or_expand(ast.file_id);
682 let ast_id_map = &db.ast_id_map(ast.file_id);
683 let span_map = &db.span_map(ast.file_id);
684
685 let node = ast_id_map.get(ast.value).to_node(&root);
686 let range = ast::HasName::name(&node)
687 .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range());
688 span_map.span_for_range(range)
689}
690
691fn expand_proc_macro(
692 db: &dyn ExpandDatabase,
693 id: MacroCallId,
694) -> ExpandResult<Arc<tt::TopSubtree>> {
695 let loc = db.lookup_intern_macro_call(id);
696 let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind);
697
698 let (ast, expander) = match loc.def.kind {
699 MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander),
700 _ => unreachable!(),
701 };
702
703 let attr_arg = match &loc.kind {
704 MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args),
705 _ => None,
706 };
707
708 let ExpandResult { value: mut tt, err } = {
709 let span = db.proc_macro_span(ast);
710 expander.expand(
711 db,
712 loc.def.krate,
713 loc.krate,
714 ¯o_arg,
715 attr_arg,
716 span_with_def_site_ctxt(db, span, id.into(), loc.def.edition),
717 span_with_call_site_ctxt(db, span, id.into(), loc.def.edition),
718 span_with_mixed_site_ctxt(db, span, id.into(), loc.def.edition),
719 )
720 };
721
722 if let Err(value) = check_tt_count(&tt) {
724 return value.map(|()| Arc::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))));
725 }
726
727 fixup::reverse_fixups(&mut tt, &undo_info);
728
729 ExpandResult { value: Arc::new(tt), err }
730}
731
732pub(crate) fn token_tree_to_syntax_node(
733 db: &dyn ExpandDatabase,
734 tt: &tt::TopSubtree,
735 expand_to: ExpandTo,
736 edition: parser::Edition,
737) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
738 let entry_point = match expand_to {
739 ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts,
740 ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems,
741 ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern,
742 ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
743 ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
744 };
745 syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db), edition)
746}
747
748fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
749 let tt = tt.top_subtree();
750 let count = tt.count();
751 if count <= TOKEN_LIMIT {
752 Ok(())
753 } else {
754 Err(ExpandResult {
755 value: (),
756 err: Some(ExpandError::other(
757 tt.delimiter.open,
758 format!(
759 "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}",
760 ),
761 )),
762 })
763 }
764}
765
766fn intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallLoc) -> MacroCallId {
767 MacroCallId::new(db, macro_call)
768}
769
770fn lookup_intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> MacroCallLoc {
771 macro_call.loc(db)
772}