1#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
7#![allow(rustdoc::private_intra_doc_links)]
9
10pub use intern;
11
12pub mod attrs;
13pub mod builtin;
14pub mod change;
15pub mod declarative;
16pub mod eager;
17pub mod files;
18pub mod hygiene;
19pub mod inert_attr_macro;
20pub mod mod_path;
21pub mod name;
22pub mod proc_macro;
23pub mod span_map;
24
25mod cfg_process;
26mod fixup;
27mod prettify_macro_expansion_;
28
29use salsa::plumbing::{AsId, FromId};
30use thin_vec::ThinVec;
31use triomphe::Arc;
32
33use core::fmt;
34use std::{borrow::Cow, ops};
35
36use base_db::{Crate, SourceDatabase};
37use either::Either;
38use mbe::MatchedArmIndex;
39use span::{
40 AstIdMap, Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span,
41 SyntaxContext,
42};
43use syntax::{
44 Parse, SyntaxError, SyntaxNode, SyntaxToken, T, TextRange, TextSize,
45 ast::{self, AstNode},
46};
47use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
48
49use crate::{
50 attrs::AttrId,
51 builtin::{
52 BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander,
53 include_input_to_file_id, pseudo_derive_attr_expansion,
54 },
55 cfg_process::attr_macro_input_to_token_tree,
56 fixup::SyntaxFixupUndoInfo,
57 hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
58 proc_macro::{CustomProcMacroExpander, ProcMacroKind, ProcMacros},
59 span_map::{ExpansionSpanMap, RealSpanMap, SpanMap},
60};
61
62pub use crate::{
63 files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
64 prettify_macro_expansion_::prettify_macro_expansion,
65};
66
67pub use base_db::EditionedFileId;
68pub use mbe::{DeclarativeMacro, MacroCallStyle, MacroCallStyles, ValueResult};
69
70pub use tt;
71
72type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span);
75
76const TOKEN_LIMIT: usize = 2_097_152;
83
84#[macro_export]
85macro_rules! impl_intern_lookup {
86 ($id:ident, $loc:ident) => {
87 impl $crate::Intern for $loc {
88 type ID = $id;
89 fn intern(self, db: &dyn ::base_db::SourceDatabase) -> Self::ID {
90 $id::new(db, self)
91 }
92 }
93
94 impl $crate::Lookup for $id {
95 type Data = $loc;
96 fn lookup<'db>(&self, db: &'db dyn ::base_db::SourceDatabase) -> &'db Self::Data {
97 self.loc(db)
98 }
99 }
100 };
101}
102
103pub trait Intern {
105 type ID;
106 fn intern(self, db: &dyn SourceDatabase) -> Self::ID;
107}
108
109pub trait Lookup {
110 type Data;
111 fn lookup<'db>(&self, db: &'db dyn SourceDatabase) -> &'db Self::Data;
112}
113
114impl_intern_lookup!(MacroCallId, MacroCallLoc);
115
116pub type ExpandResult<T> = ValueResult<T, ExpandError>;
117
118#[derive(Debug, PartialEq, Eq, Clone, Hash)]
119pub struct ExpandError {
120 inner: Arc<(ExpandErrorKind, Span)>,
121}
122
123impl ExpandError {
124 pub fn new(span: Span, kind: ExpandErrorKind) -> Self {
125 ExpandError { inner: Arc::new((kind, span)) }
126 }
127 pub fn other(span: Span, msg: impl Into<Box<str>>) -> Self {
128 ExpandError { inner: Arc::new((ExpandErrorKind::Other(msg.into()), span)) }
129 }
130 pub fn kind(&self) -> &ExpandErrorKind {
131 &self.inner.0
132 }
133 pub fn span(&self) -> Span {
134 self.inner.1
135 }
136
137 pub fn render_to_string(&self, db: &dyn SourceDatabase) -> RenderedExpandError {
138 self.inner.0.render_to_string(db)
139 }
140}
141
142#[derive(Debug, PartialEq, Eq, Clone, Hash)]
143pub enum ExpandErrorKind {
144 ProcMacroAttrExpansionDisabled,
146 MissingProcMacroExpander(Crate),
147 MacroDisabled,
149 MacroDefinition,
151 Mbe(mbe::ExpandErrorKind),
152 RecursionOverflow,
153 Other(Box<str>),
154 ProcMacroPanic(Box<str>),
155}
156
157pub struct RenderedExpandError {
158 pub message: String,
159 pub error: bool,
160 pub kind: &'static str,
161}
162
163impl fmt::Display for RenderedExpandError {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 write!(f, "{}", self.message)
166 }
167}
168
169impl RenderedExpandError {
170 const GENERAL_KIND: &str = "macro-error";
171 const DISABLED: &str = "proc-macro-disabled";
172 const ATTR_EXP_DISABLED: &str = "attribute-expansion-disabled";
173}
174
175impl ExpandErrorKind {
176 pub fn render_to_string(&self, db: &dyn SourceDatabase) -> RenderedExpandError {
177 match self {
178 ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
179 message: "procedural attribute macro expansion is disabled".to_owned(),
180 error: false,
181 kind: RenderedExpandError::ATTR_EXP_DISABLED,
182 },
183 ExpandErrorKind::MacroDisabled => RenderedExpandError {
184 message: "proc-macro is explicitly disabled".to_owned(),
185 error: false,
186 kind: RenderedExpandError::DISABLED,
187 },
188 &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
189 match ProcMacros::get_for_crate(db, def_crate).and_then(|it| it.get_error()) {
190 Some(e) => RenderedExpandError {
191 message: e.to_string(),
192 error: e.is_hard_error(),
193 kind: RenderedExpandError::GENERAL_KIND,
194 },
195 None => RenderedExpandError {
196 message: format!(
197 "internal error: proc-macro map is missing error entry for crate {def_crate:?}"
198 ),
199 error: true,
200 kind: RenderedExpandError::GENERAL_KIND,
201 },
202 }
203 }
204 ExpandErrorKind::MacroDefinition => RenderedExpandError {
205 message: "macro definition has parse errors".to_owned(),
206 error: true,
207 kind: RenderedExpandError::GENERAL_KIND,
208 },
209 ExpandErrorKind::Mbe(e) => RenderedExpandError {
210 message: e.to_string(),
211 error: true,
212 kind: RenderedExpandError::GENERAL_KIND,
213 },
214 ExpandErrorKind::RecursionOverflow => RenderedExpandError {
215 message: "overflow expanding the original macro".to_owned(),
216 error: true,
217 kind: RenderedExpandError::GENERAL_KIND,
218 },
219 ExpandErrorKind::Other(e) => RenderedExpandError {
220 message: (**e).to_owned(),
221 error: true,
222 kind: RenderedExpandError::GENERAL_KIND,
223 },
224 ExpandErrorKind::ProcMacroPanic(e) => RenderedExpandError {
225 message: format!("proc-macro panicked: {e}"),
226 error: true,
227 kind: RenderedExpandError::GENERAL_KIND,
228 },
229 }
230 }
231}
232
233impl From<mbe::ExpandError> for ExpandError {
234 fn from(mbe: mbe::ExpandError) -> Self {
235 ExpandError { inner: Arc::new((ExpandErrorKind::Mbe(mbe.inner.1.clone()), mbe.inner.0)) }
236 }
237}
238#[derive(Debug, Clone, PartialEq, Eq, Hash)]
239pub struct MacroCallLoc {
240 pub def: MacroDefId,
241 pub krate: Crate,
242 pub kind: MacroCallKind,
243 pub ctxt: SyntaxContext,
244 pub macro_depth: u32,
249}
250
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub struct MacroDefId {
253 pub krate: Crate,
254 pub edition: Edition,
255 pub kind: MacroDefKind,
256 pub local_inner: bool,
257 pub allow_internal_unsafe: bool,
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
261pub enum MacroDefKind {
262 Declarative(AstId<ast::Macro>, MacroCallStyles),
263 BuiltIn(AstId<ast::Macro>, BuiltinFnLikeExpander),
264 BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander),
265 BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander),
266 BuiltInEager(AstId<ast::Macro>, EagerExpander),
267 UnimplementedBuiltIn(AstId<ast::Macro>),
268 ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
269}
270
271impl MacroDefKind {
272 #[inline]
273 pub fn is_declarative(&self) -> bool {
274 matches!(self, MacroDefKind::Declarative(..))
275 }
276
277 pub fn erased_ast_id(&self) -> ErasedAstId {
278 match *self {
279 MacroDefKind::ProcMacro(id, ..) => id.erase(),
280 MacroDefKind::BuiltIn(id, _)
281 | MacroDefKind::BuiltInAttr(id, _)
282 | MacroDefKind::BuiltInDerive(id, _)
283 | MacroDefKind::BuiltInEager(id, _)
284 | MacroDefKind::Declarative(id, ..)
285 | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(),
286 }
287 }
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Hash)]
291pub struct EagerCallInfo {
292 arg: tt::TopSubtree,
294 arg_id: MacroCallId,
296 error: Option<ExpandError>,
297 span: Span,
299}
300
301#[derive(Debug, Clone, PartialEq, Eq, Hash)]
302pub enum MacroCallKind {
303 FnLike {
304 ast_id: AstId<ast::MacroCall>,
305 expand_to: ExpandTo,
306 eager: Option<Box<EagerCallInfo>>,
311 },
312 Derive {
313 ast_id: AstId<ast::Adt>,
314 derive_attr_index: AttrId,
316 derive_index: u32,
318 derive_macro_id: MacroCallId,
321 },
322 Attr {
323 ast_id: AstId<ast::Item>,
324 attr_args: Option<Box<tt::TopSubtree>>,
326 censored_attr_ids: AttrMacroAttrIds,
334 },
335}
336
337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
338pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr);
339
340impl AttrMacroAttrIds {
341 #[inline]
342 pub fn from_one(id: AttrId) -> Self {
343 Self(AttrMacroAttrIdsRepr::One(id))
344 }
345
346 #[inline]
347 pub fn from_many(ids: &[AttrId]) -> Self {
348 if let &[id] = ids {
349 Self(AttrMacroAttrIdsRepr::One(id))
350 } else {
351 Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect()))
352 }
353 }
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Hash)]
357enum AttrMacroAttrIdsRepr {
358 One(AttrId),
359 ManyDerives(ThinVec<AttrId>),
360}
361
362impl ops::Deref for AttrMacroAttrIds {
363 type Target = [AttrId];
364
365 #[inline]
366 fn deref(&self) -> &Self::Target {
367 match &self.0 {
368 AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one),
369 AttrMacroAttrIdsRepr::ManyDerives(many) => many,
370 }
371 }
372}
373
374impl AttrMacroAttrIds {
375 #[inline]
376 pub fn invoc_attr(&self) -> AttrId {
377 match &self.0 {
378 AttrMacroAttrIdsRepr::One(it) => *it,
379 AttrMacroAttrIdsRepr::ManyDerives(it) => {
380 *it.last().expect("should always have at least one `AttrId`")
381 }
382 }
383 }
384}
385
386impl MacroCallKind {
387 pub(crate) fn call_style(&self) -> MacroCallStyle {
388 match self {
389 MacroCallKind::FnLike { .. } => MacroCallStyle::FnLike,
390 MacroCallKind::Derive { .. } => MacroCallStyle::Derive,
391 MacroCallKind::Attr { .. } => MacroCallStyle::Attr,
392 }
393 }
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
397pub enum MacroKind {
398 Declarative,
400 DeclarativeBuiltIn,
402 Derive,
404 DeriveBuiltIn,
406 Attr,
408 AttrBuiltIn,
410 ProcMacro,
412}
413
414impl MacroCallId {
415 pub fn call_node(self, db: &dyn SourceDatabase) -> InFile<SyntaxNode> {
416 self.loc(db).to_node(db)
417 }
418 pub fn expansion_level(self, db: &dyn SourceDatabase) -> u32 {
419 let mut level = 0;
420 let mut macro_file = self;
421 loop {
422 let loc = macro_file.loc(db);
423
424 level += 1;
425 macro_file = match loc.kind.file_id() {
426 HirFileId::FileId(_) => break level,
427 HirFileId::MacroFile(it) => it,
428 };
429 }
430 }
431 pub fn parent(self, db: &dyn SourceDatabase) -> HirFileId {
432 self.loc(db).kind.file_id()
433 }
434
435 pub fn expansion_info(self, db: &dyn SourceDatabase) -> ExpansionInfo<'_> {
437 ExpansionInfo::new(db, self)
438 }
439
440 pub fn kind(self, db: &dyn SourceDatabase) -> MacroKind {
441 match self.loc(db).def.kind {
442 MacroDefKind::Declarative(..) => MacroKind::Declarative,
443 MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
444 MacroKind::DeclarativeBuiltIn
445 }
446 MacroDefKind::BuiltInDerive(..) => MacroKind::DeriveBuiltIn,
447 MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => MacroKind::Derive,
448 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr,
449 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro,
450 MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn,
451 MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative,
452 }
453 }
454
455 pub fn is_include_macro(self, db: &dyn SourceDatabase) -> bool {
456 self.loc(db).def.is_include()
457 }
458
459 pub fn is_include_like_macro(self, db: &dyn SourceDatabase) -> bool {
460 self.loc(db).def.is_include_like()
461 }
462
463 pub fn is_env_or_option_env(self, db: &dyn SourceDatabase) -> bool {
464 self.loc(db).def.is_env_or_option_env()
465 }
466
467 pub fn is_eager(self, db: &dyn SourceDatabase) -> bool {
468 let loc = self.loc(db);
469 matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
470 }
471
472 pub fn eager_arg(self, db: &dyn SourceDatabase) -> Option<MacroCallId> {
473 let loc = self.loc(db);
474 match &loc.kind {
475 MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
476 _ => None,
477 }
478 }
479
480 pub fn is_derive_attr_pseudo_expansion(self, db: &dyn SourceDatabase) -> bool {
481 let loc = self.loc(db);
482 loc.def.is_attribute_derive()
483 }
484}
485
486#[salsa::tracked]
487impl MacroCallId {
488 #[salsa::tracked(returns(ref), lru = 512)]
492 pub fn parse_macro_expansion(
493 self,
494 db: &dyn SourceDatabase,
495 ) -> ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)> {
496 let _p = tracing::info_span!("parse_macro_expansion").entered();
497 let loc = self.loc(db);
498 let expand_to = loc.expand_to();
499 let mbe::ValueResult { value: (tt, matched_arm), err } = self.macro_expand(db, loc);
500
501 let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to);
502 rev_token_map.matched_arm = matched_arm;
503
504 ExpandResult { value: (parse, rev_token_map), err }
505 }
506
507 pub fn parse_macro_expansion_error(
508 self,
509 db: &dyn SourceDatabase,
510 ) -> Option<ExpandResult<Arc<[SyntaxError]>>> {
511 let e: ExpandResult<Arc<[SyntaxError]>> =
512 self.parse_macro_expansion(db).as_ref().map(|it| Arc::from(it.0.errors()));
513 if e.value.is_empty() && e.err.is_none() { None } else { Some(e) }
514 }
515
516 #[expect(deprecated, reason = "we are `macro_arg_considering_derives`")]
523 pub fn macro_arg_considering_derives<'db>(
524 self,
525 db: &'db dyn SourceDatabase,
526 kind: &MacroCallKind,
527 ) -> &'db MacroArgResult {
528 match kind {
529 MacroCallKind::Derive { derive_macro_id, .. } => derive_macro_id.macro_arg(db),
531 _ => self.macro_arg(db),
533 }
534 }
535
536 #[salsa::tracked(returns(ref))]
540 #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
541 fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult {
542 let loc = self.loc(db);
543
544 if let MacroCallLoc {
545 def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
546 kind: MacroCallKind::FnLike { eager: Some(eager), .. },
547 ..
548 } = &loc
549 {
550 return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span);
551 }
552
553 let (parse, map) = loc.kind.file_id().parse_with_map(db);
554 let root = parse.syntax_node();
555
556 let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind {
557 MacroCallKind::FnLike { ast_id, .. } => {
558 let node = &ast_id.to_ptr(db).to_node(&root);
559 let path_range = node
560 .path()
561 .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range());
562 let span = map.span_for_range(path_range);
563
564 let dummy_tt = |kind| {
565 (
566 tt::TopSubtree::from_token_trees(
567 tt::Delimiter { open: span, close: span, kind },
568 tt::TokenTreesView::empty(),
569 ),
570 SyntaxFixupUndoInfo::default(),
571 span,
572 )
573 };
574
575 let Some(tt) = node.token_tree() else {
576 return dummy_tt(tt::DelimiterKind::Invisible);
577 };
578 let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
579 let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
580
581 let mismatched_delimiters = !matches!(
582 (first, last),
583 (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
584 );
585 if mismatched_delimiters {
586 cov_mark::hit!(issue9358_bad_macro_stack_overflow);
593
594 let kind = match first {
595 _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
596 T!['('] => tt::DelimiterKind::Parenthesis,
597 T!['['] => tt::DelimiterKind::Bracket,
598 T!['{'] => tt::DelimiterKind::Brace,
599 _ => tt::DelimiterKind::Invisible,
600 };
601 return dummy_tt(kind);
602 }
603
604 let mut tt = syntax_bridge::syntax_node_to_token_tree(
605 tt.syntax(),
606 map,
607 span,
608 if loc.def.is_proc_macro() {
609 DocCommentDesugarMode::ProcMacro
610 } else {
611 DocCommentDesugarMode::Mbe
612 },
613 );
614 if loc.def.is_proc_macro() {
615 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
617 }
618 return (tt, SyntaxFixupUndoInfo::NONE, span);
619 }
620 MacroCallKind::Derive { .. } => {
622 unreachable!("`MacroCallId::macro_arg` called with `MacroCallKind::Derive`")
623 }
624 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
625 let node = ast_id.to_ptr(db).to_node(&root);
626 let (_, attr) =
627 attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node);
628 let range = attr
629 .path()
630 .map(|path| path.syntax().text_range())
631 .unwrap_or_else(|| attr.syntax().text_range());
632 let span = map.span_for_range(range);
633
634 let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
635 (is_derive, &**attr_ids, node, span)
636 }
637 };
638
639 let (mut tt, undo_info) = attr_macro_input_to_token_tree(
640 db,
641 item_node.syntax(),
642 map,
643 span,
644 is_derive,
645 censor_item_tree_attr_ids,
646 loc.krate,
647 );
648
649 if loc.def.is_proc_macro() {
650 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
652 }
653
654 (tt, undo_info, span)
655 }
656
657 fn macro_expand<'db>(
658 self,
659 db: &'db dyn SourceDatabase,
660 loc: &MacroCallLoc,
661 ) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> {
662 let _p = tracing::info_span!("macro_expand").entered();
663
664 let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
665 MacroDefKind::ProcMacro(..) => {
666 return self.expand_proc_macro(db).as_ref().map(|it| (Cow::Borrowed(it), None));
667 }
668 _ => {
669 let (macro_arg, undo_info, span) =
670 self.macro_arg_considering_derives(db, &loc.kind);
671 let span = *span;
672
673 let arg = macro_arg;
674 let res = match loc.def.kind {
675 MacroDefKind::Declarative(id, _) => {
676 id.decl_macro_expander(db, loc.def.krate).expand(db, arg, self, span)
677 }
678 MacroDefKind::BuiltIn(_, it) => {
679 it.expand(db, self, arg, span).map_err(Into::into).zip_val(None)
680 }
681 MacroDefKind::BuiltInDerive(_, it) => {
682 it.expand(db, self, arg, span).map_err(Into::into).zip_val(None)
683 }
684 MacroDefKind::UnimplementedBuiltIn(_) => {
685 expand_unimplemented_builtin_macro(span).zip_val(None)
686 }
687 MacroDefKind::BuiltInEager(_, it) => {
688 let eager = match &loc.kind {
695 MacroCallKind::FnLike { eager: None, .. } => {
696 return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None);
697 }
698 MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
699 _ => None,
700 };
701
702 let mut res = it.expand(db, self, arg, span).map_err(Into::into);
703
704 if let Some(EagerCallInfo { error, .. }) = eager {
705 res.err = error.clone().or(res.err);
707 }
708 res.zip_val(None)
709 }
710 MacroDefKind::BuiltInAttr(_, it) => {
711 let mut res = it.expand(db, self, arg, span);
712 fixup::reverse_fixups(&mut res.value, undo_info);
713 res.zip_val(None)
714 }
715 MacroDefKind::ProcMacro(_, _, _) => unreachable!(),
716 };
717 (res, span)
718 }
719 };
720
721 if !loc.def.is_include() {
723 if let Err(value) = check_tt_count(&tt) {
725 return value
726 .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))))
727 .zip_val(matched_arm);
728 }
729 }
730
731 ExpandResult { value: (Cow::Owned(tt), matched_arm), err }
732 }
733
734 #[salsa::tracked(returns(ref))]
739 fn expand_proc_macro(self, db: &dyn SourceDatabase) -> ExpandResult<tt::TopSubtree> {
740 let loc = self.loc(db);
741 let (macro_arg, undo_info, span) = self.macro_arg_considering_derives(db, &loc.kind);
742
743 let (ast, expander) = match loc.def.kind {
744 MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander),
745 _ => unreachable!(),
746 };
747
748 let attr_arg = match &loc.kind {
749 MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args),
750 _ => None,
751 };
752
753 let ExpandResult { value: mut tt, err } = {
754 let span = proc_macro_span(db, ast);
755 expander.expand(
756 db,
757 loc.def.krate,
758 loc.krate,
759 macro_arg,
760 attr_arg,
761 span_with_def_site_ctxt(db, span, self.into(), loc.def.edition),
762 span_with_call_site_ctxt(db, span, self.into(), loc.def.edition),
763 span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition),
764 )
765 };
766
767 if let Err(value) = check_tt_count(&tt) {
769 return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span)));
770 }
771
772 fixup::reverse_fixups(&mut tt, undo_info);
773
774 ExpandResult { value: tt, err }
775 }
776}
777
778impl MacroCallId {
779 pub fn expand_speculative(
784 self,
785 db: &dyn SourceDatabase,
786 speculative_args: &SyntaxNode,
787 token_to_map: SyntaxToken,
788 ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
789 let loc = self.loc(db);
790 let (_, _, span) = *self.macro_arg_considering_derives(db, &loc.kind);
791
792 let span_map = RealSpanMap::absolute(span.anchor.file_id);
793 let span_map = SpanMap::RealSpanMap(&span_map);
794
795 let (mut tt, undo_info) = match &loc.kind {
797 MacroCallKind::FnLike { .. } => (
798 syntax_bridge::syntax_node_to_token_tree(
799 speculative_args,
800 span_map,
801 span,
802 if loc.def.is_proc_macro() {
803 DocCommentDesugarMode::ProcMacro
804 } else {
805 DocCommentDesugarMode::Mbe
806 },
807 ),
808 SyntaxFixupUndoInfo::NONE,
809 ),
810 MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
811 syntax_bridge::syntax_node_to_token_tree(
812 speculative_args,
813 span_map,
814 span,
815 DocCommentDesugarMode::ProcMacro,
816 ),
817 SyntaxFixupUndoInfo::NONE,
818 ),
819 MacroCallKind::Derive { derive_macro_id, .. } => {
820 let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } =
821 &derive_macro_id.loc(db).kind
822 else {
823 unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`");
824 };
825 attr_macro_input_to_token_tree(
826 db,
827 speculative_args,
828 span_map,
829 span,
830 true,
831 attr_ids,
832 loc.krate,
833 )
834 }
835 MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
836 attr_macro_input_to_token_tree(
837 db,
838 speculative_args,
839 span_map,
840 span,
841 false,
842 attr_ids,
843 loc.krate,
844 )
845 }
846 };
847
848 let attr_arg = match &loc.kind {
849 MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
850 if loc.def.is_attribute_derive() {
851 ast::Attr::cast(speculative_args.clone())
853 .and_then(|attr| {
854 if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? {
855 meta.token_tree()
856 } else {
857 None
858 }
859 })
860 .map(|token_tree| {
861 let mut tree = syntax_node_to_token_tree(
862 token_tree.syntax(),
863 span_map,
864 span,
865 DocCommentDesugarMode::ProcMacro,
866 );
867 tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
868 tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
869 tree
870 })
871 } else {
872 let item = ast::Item::cast(speculative_args.clone())?;
875 let (_, meta) = attr_ids
876 .invoc_attr()
877 .find_attr_range_with_source_opt(db, loc.krate, &item)?;
878 if let ast::Meta::TokenTreeMeta(meta) = meta
879 && let Some(tt) = meta.token_tree()
880 {
881 let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
882 tt.syntax(),
883 span_map,
884 span,
885 DocCommentDesugarMode::ProcMacro,
886 );
887 attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
888 Some(attr_arg)
889 } else {
890 None
891 }
892 }
893 }
894 _ => None,
895 };
896
897 let mut speculative_expansion = match loc.def.kind {
900 MacroDefKind::ProcMacro(ast, expander, _) => {
901 let span = proc_macro_span(db, ast);
902 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
903 tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
904 expander.expand(
905 db,
906 loc.def.krate,
907 loc.krate,
908 &tt,
909 attr_arg.as_ref(),
910 span_with_def_site_ctxt(db, span, self.into(), loc.def.edition),
911 span_with_call_site_ctxt(db, span, self.into(), loc.def.edition),
912 span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition),
913 )
914 }
915 MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
916 pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
917 }
918 MacroDefKind::Declarative(it, _) => it
919 .decl_macro_expander(db, loc.krate)
920 .expand_unhygienic(db, &tt, loc.kind.call_style(), span),
921 MacroDefKind::BuiltIn(_, it) => it.expand(db, self, &tt, span).map_err(Into::into),
922 MacroDefKind::BuiltInDerive(_, it) => {
923 it.expand(db, self, &tt, span).map_err(Into::into)
924 }
925 MacroDefKind::BuiltInEager(_, it) => it.expand(db, self, &tt, span).map_err(Into::into),
926 MacroDefKind::BuiltInAttr(_, it) => it.expand(db, self, &tt, span),
927 MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span),
928 };
929
930 let expand_to = loc.expand_to();
931
932 fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
933 let (node, rev_tmap) =
934 token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to);
935
936 let syntax_node = node.syntax_node();
937 let token = rev_tmap
938 .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
939 .filter_map(|(range, ctx)| {
940 syntax_node.covering_element(range).into_token().zip(Some(ctx))
941 })
942 .map(|(t, ctx)| {
943 let ranking = ctx.is_opaque(db) as u8
947 + 2 * (t.kind() != token_to_map.kind()) as u8
948 + 4 * ((t.text() != token_to_map.text()) as u8);
949 (t, ranking)
950 })
951 .collect();
952 Some((node.syntax_node(), token))
953 }
954}
955
956fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult<tt::TopSubtree> {
957 ExpandResult::new(
958 tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
959 ExpandError::other(span, "this built-in macro is not implemented"),
960 )
961}
962
963fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>) -> Span {
969 #[salsa::tracked(returns(copy))]
970 fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>, _: ()) -> Span {
971 let (parse, span_map) = ast.file_id.parse_with_map(db);
972 let root = parse.syntax_node();
973 let ast_id_map = ast.file_id.ast_id_map(db);
974
975 let node = ast_id_map.get(ast.value).to_node(&root);
976 let range = ast::HasName::name(&node)
977 .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range());
978 span_map.span_for_range(range)
979 }
980 proc_macro_span(db, ast, ())
981}
982
983pub(crate) fn token_tree_to_syntax_node(
984 db: &dyn SourceDatabase,
985 tt: &tt::TopSubtree,
986 expand_to: ExpandTo,
987) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
988 let entry_point = match expand_to {
989 ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts,
990 ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems,
991 ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern,
992 ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
993 ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
994 };
995 syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db))
996}
997
998fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
999 let tt = tt.top_subtree();
1000 let count = tt.count();
1001 if count <= TOKEN_LIMIT {
1002 Ok(())
1003 } else {
1004 Err(ExpandResult {
1005 value: (),
1006 err: Some(ExpandError::other(
1007 tt.delimiter.open,
1008 format!(
1009 "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}",
1010 ),
1011 )),
1012 })
1013 }
1014}
1015
1016impl MacroDefId {
1017 pub fn make_call(
1018 self,
1019 db: &dyn SourceDatabase,
1020 krate: Crate,
1021 kind: MacroCallKind,
1022 ctxt: SyntaxContext,
1023 macro_depth: u32,
1024 ) -> MacroCallId {
1025 MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt, macro_depth })
1026 }
1027
1028 pub fn definition_range(&self, db: &dyn SourceDatabase) -> InFile<TextRange> {
1029 match self.kind {
1030 MacroDefKind::Declarative(id, _)
1031 | MacroDefKind::BuiltIn(id, _)
1032 | MacroDefKind::BuiltInAttr(id, _)
1033 | MacroDefKind::BuiltInDerive(id, _)
1034 | MacroDefKind::BuiltInEager(id, _)
1035 | MacroDefKind::UnimplementedBuiltIn(id) => {
1036 id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range())
1037 }
1038 MacroDefKind::ProcMacro(id, _, _) => {
1039 id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range())
1040 }
1041 }
1042 }
1043
1044 pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
1045 match self.kind {
1046 MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
1047 MacroDefKind::Declarative(id, _)
1048 | MacroDefKind::BuiltIn(id, _)
1049 | MacroDefKind::BuiltInAttr(id, _)
1050 | MacroDefKind::BuiltInDerive(id, _)
1051 | MacroDefKind::BuiltInEager(id, _)
1052 | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id),
1053 }
1054 }
1055
1056 pub fn is_proc_macro(&self) -> bool {
1057 matches!(self.kind, MacroDefKind::ProcMacro(..))
1058 }
1059
1060 pub fn is_attribute(&self) -> bool {
1061 match self.kind {
1062 MacroDefKind::BuiltInAttr(..)
1063 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
1064 | MacroDefKind::UnimplementedBuiltIn(_) => true,
1065 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR),
1066 _ => false,
1067 }
1068 }
1069
1070 pub fn is_derive(&self) -> bool {
1071 match self.kind {
1072 MacroDefKind::BuiltInDerive(..)
1073 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
1074 | MacroDefKind::UnimplementedBuiltIn(_) => true,
1075 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE),
1076 _ => false,
1077 }
1078 }
1079
1080 pub fn is_fn_like(&self) -> bool {
1081 matches!(
1082 self.kind,
1083 MacroDefKind::BuiltIn(..)
1084 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
1085 | MacroDefKind::BuiltInEager(..)
1086 | MacroDefKind::Declarative(..)
1087 | MacroDefKind::UnimplementedBuiltIn(_)
1088 )
1089 }
1090
1091 pub fn is_attribute_derive(&self) -> bool {
1092 matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
1093 }
1094
1095 pub fn is_include(&self) -> bool {
1096 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
1097 }
1098
1099 pub fn is_include_like(&self) -> bool {
1100 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
1101 }
1102
1103 pub fn is_env_or_option_env(&self) -> bool {
1104 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
1105 }
1106}
1107
1108impl MacroCallLoc {
1109 pub fn to_node(&self, db: &dyn SourceDatabase) -> InFile<SyntaxNode> {
1110 match &self.kind {
1111 MacroCallKind::FnLike { ast_id, .. } => {
1112 ast_id.with_value(ast_id.to_node(db).syntax().clone())
1113 }
1114 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
1115 let (_, attr) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
1116 ast_id.with_value(attr.syntax().clone())
1117 }
1118 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
1119 if self.def.is_attribute_derive() {
1120 let (_, attr) = attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
1121 ast_id.with_value(attr.syntax().clone())
1122 } else {
1123 ast_id.with_value(ast_id.to_node(db).syntax().clone())
1124 }
1125 }
1126 }
1127 }
1128
1129 pub fn to_node_item(&self, db: &dyn SourceDatabase) -> InFile<ast::Item> {
1130 match self.kind {
1131 MacroCallKind::FnLike { ast_id, .. } => {
1132 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
1133 }
1134 MacroCallKind::Derive { ast_id, .. } => {
1135 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
1136 }
1137 MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
1138 }
1139 }
1140
1141 fn expand_to(&self) -> ExpandTo {
1142 match self.kind {
1143 MacroCallKind::FnLike { expand_to, .. } => expand_to,
1144 MacroCallKind::Derive { .. } => ExpandTo::Items,
1145 MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
1146 MacroCallKind::Attr { .. } => {
1147 ExpandTo::Items
1149 }
1150 }
1151 }
1152
1153 pub fn include_file_id(
1154 &self,
1155 db: &dyn SourceDatabase,
1156 macro_call_id: MacroCallId,
1157 ) -> Option<EditionedFileId> {
1158 if self.def.is_include()
1159 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind
1160 && let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg)
1161 {
1162 return Some(it);
1163 }
1164
1165 None
1166 }
1167}
1168
1169impl MacroCallKind {
1170 pub fn descr(&self) -> &'static str {
1171 match self {
1172 MacroCallKind::FnLike { .. } => "macro call",
1173 MacroCallKind::Derive { .. } => "derive macro",
1174 MacroCallKind::Attr { .. } => "attribute macro",
1175 }
1176 }
1177
1178 pub fn file_id(&self) -> HirFileId {
1180 match *self {
1181 MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
1182 | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
1183 | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
1184 }
1185 }
1186
1187 pub fn erased_ast_id(&self) -> ErasedFileAstId {
1188 match *self {
1189 MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
1190 MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
1191 MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
1192 }
1193 }
1194
1195 pub fn original_call_range_with_input(&self, db: &dyn SourceDatabase) -> FileRange {
1202 let get_range = |kind: &_| match kind {
1203 MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(),
1204 MacroCallKind::Derive { ast_id, .. } => ast_id.erase(),
1205 MacroCallKind::Attr { ast_id, .. } => ast_id.erase(),
1206 };
1207
1208 let mut ast_id = get_range(self);
1209 let mut file_id = self.file_id();
1210 let file_id = loop {
1211 match file_id {
1212 HirFileId::MacroFile(file) => {
1213 let kind = &file.loc(db).kind;
1214 ast_id = get_range(kind);
1215 file_id = kind.file_id();
1216 }
1217 HirFileId::FileId(file_id) => break file_id,
1218 }
1219 };
1220
1221 FileRange { range: ast_id.to_ptr(db).text_range(), file_id }
1222 }
1223
1224 pub fn original_call_range(&self, db: &dyn SourceDatabase, krate: Crate) -> FileRange {
1230 let get_range = |kind: &_| match kind {
1231 MacroCallKind::FnLike { ast_id, .. } => {
1232 let node = ast_id.to_node(db);
1233 node.path()
1234 .unwrap()
1235 .syntax()
1236 .text_range()
1237 .cover(node.excl_token().unwrap().text_range())
1238 }
1239 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
1240 derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range()
1242 }
1243 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
1245 attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range()
1246 }
1247 };
1248
1249 let mut range = get_range(self);
1250 let mut file_id = self.file_id();
1251 let file_id = loop {
1252 match file_id {
1253 HirFileId::MacroFile(file) => {
1254 let kind = &file.loc(db).kind;
1255 range = get_range(kind);
1256 file_id = kind.file_id();
1257 }
1258 HirFileId::FileId(file_id) => break file_id,
1259 }
1260 };
1261
1262 FileRange { range, file_id }
1263 }
1264
1265 fn arg(&self, db: &dyn SourceDatabase) -> InFile<Option<SyntaxNode>> {
1266 match self {
1267 MacroCallKind::FnLike { ast_id, .. } => {
1268 ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
1269 }
1270 MacroCallKind::Derive { ast_id, .. } => {
1271 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
1272 }
1273 MacroCallKind::Attr { ast_id, .. } => {
1274 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
1275 }
1276 }
1277 }
1278}
1279
1280#[derive(Clone, Debug, PartialEq, Eq)]
1284pub struct ExpansionInfo<'db> {
1285 expanded: InMacroFile<SyntaxNode>,
1286 arg: InFile<Option<SyntaxNode>>,
1288 exp_map: &'db ExpansionSpanMap,
1289 arg_map: SpanMap<'db>,
1290 loc: &'db MacroCallLoc,
1291}
1292
1293impl<'db> ExpansionInfo<'db> {
1294 pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
1295 self.expanded.clone()
1296 }
1297
1298 pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
1299 self.arg.as_ref().map(|it| it.as_ref())
1300 }
1301
1302 pub fn call_file(&self) -> HirFileId {
1303 self.arg.file_id
1304 }
1305
1306 pub fn is_attr(&self) -> bool {
1307 matches!(
1308 self.loc.def.kind,
1309 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
1310 )
1311 }
1312
1313 pub fn map_range_down_exact(
1319 &self,
1320 span: Span,
1321 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
1322 if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
1323 return None;
1324 }
1325
1326 let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
1327 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
1328 });
1329
1330 Some(InMacroFile::new(self.expanded.file_id, tokens))
1331 }
1332
1333 pub fn map_range_down(
1338 &self,
1339 span: Span,
1340 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
1341 if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
1342 return None;
1343 }
1344
1345 let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
1346 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
1347 });
1348
1349 Some(InMacroFile::new(self.expanded.file_id, tokens))
1350 }
1351
1352 pub fn span_for_offset(
1354 &self,
1355 db: &dyn SourceDatabase,
1356 offset: TextSize,
1357 ) -> (FileRange, SyntaxContext) {
1358 debug_assert!(self.expanded.value.text_range().contains(offset));
1359 span_for_offset(db, self.exp_map, offset)
1360 }
1361
1362 pub fn map_node_range_up(
1364 &self,
1365 db: &dyn SourceDatabase,
1366 range: TextRange,
1367 ) -> Option<(FileRange, SyntaxContext)> {
1368 debug_assert!(self.expanded.value.text_range().contains_range(range));
1369 map_node_range_up(db, self.exp_map, range)
1370 }
1371
1372 pub fn map_range_up_once(
1377 &self,
1378 db: &dyn SourceDatabase,
1379 token: TextRange,
1380 ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
1381 debug_assert!(self.expanded.value.text_range().contains_range(token));
1382 let span = self.exp_map.span_at(token.start());
1383 match &self.arg_map {
1384 SpanMap::RealSpanMap(_) => {
1385 let range = resolve_span(db, span);
1386 InFile { file_id: range.file_id.into(), value: smallvec::smallvec![range.range] }
1387 }
1388 SpanMap::ExpansionSpanMap(arg_map) => {
1389 let Some(arg_node) = &self.arg.value else {
1390 return InFile::new(self.arg.file_id, smallvec::smallvec![]);
1391 };
1392 let arg_range = arg_node.text_range();
1393 InFile::new(
1394 self.arg.file_id,
1395 arg_map
1396 .ranges_with_span_exact(span)
1397 .map(|(range, _)| range)
1398 .filter(|range| range.intersect(arg_range).is_some())
1399 .collect(),
1400 )
1401 }
1402 }
1403 }
1404
1405 pub fn new(db: &'db dyn SourceDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> {
1406 let _p = tracing::info_span!("ExpansionInfo::new").entered();
1407 let loc = macro_file.loc(db);
1408
1409 let arg_tt = loc.kind.arg(db);
1410 let arg_map = arg_tt.file_id.span_map(db);
1411
1412 let (parse, exp_map) = ¯o_file.parse_macro_expansion(db).value;
1413 let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
1414
1415 ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
1416 }
1417}
1418
1419pub fn map_node_range_up_rooted(
1423 db: &dyn SourceDatabase,
1424 exp_map: &ExpansionSpanMap,
1425 range: TextRange,
1426) -> Option<FileRange> {
1427 let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
1428 let Span { range, anchor, ctx } = spans.next()?;
1429 let mut start = range.start();
1430 let mut end = range.end();
1431
1432 for span in spans {
1433 if span.anchor != anchor {
1434 return None;
1435 }
1436 start = start.min(span.range.start());
1437 end = end.max(span.range.end());
1438 }
1439 Some(resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx }))
1440}
1441
1442pub fn map_node_range_up(
1446 db: &dyn SourceDatabase,
1447 exp_map: &ExpansionSpanMap,
1448 range: TextRange,
1449) -> Option<(FileRange, SyntaxContext)> {
1450 let mut spans = exp_map.spans_for_range(range);
1451 let Span { range, anchor, ctx } = spans.next()?;
1452 let mut start = range.start();
1453 let mut end = range.end();
1454
1455 for span in spans {
1456 if span.anchor != anchor || span.ctx != ctx {
1457 return None;
1458 }
1459 start = start.min(span.range.start());
1460 end = end.max(span.range.end());
1461 }
1462 Some((resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx }), ctx))
1463}
1464
1465pub fn span_for_offset(
1467 db: &dyn SourceDatabase,
1468 exp_map: &ExpansionSpanMap,
1469 offset: TextSize,
1470) -> (FileRange, SyntaxContext) {
1471 let span = exp_map.span_at(offset);
1472 (resolve_span(db, span), span.ctx)
1473}
1474
1475pub fn resolve_span(db: &dyn SourceDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange {
1479 let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id);
1480 let anchor_offset =
1481 HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start();
1482 FileRange { file_id, range: range + anchor_offset }
1483}
1484
1485#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1501pub enum ExpandTo {
1502 Statements,
1503 Items,
1504 Pattern,
1505 Type,
1506 Expr,
1507}
1508
1509impl ExpandTo {
1510 pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1511 use syntax::SyntaxKind::*;
1512
1513 let syn = call.syntax();
1514
1515 let parent = match syn.parent() {
1516 Some(it) => it,
1517 None => return ExpandTo::Statements,
1518 };
1519
1520 if parent.kind() == MACRO_EXPR
1523 && parent
1524 .parent()
1525 .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1526 {
1527 return ExpandTo::Statements;
1528 }
1529
1530 match parent.kind() {
1531 MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1532 MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1533 MACRO_PAT => ExpandTo::Pattern,
1534 MACRO_TYPE => ExpandTo::Type,
1535
1536 ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1537 | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1538 | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1539 | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1540 | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1541 _ => {
1542 ExpandTo::Items
1544 }
1545 }
1546 }
1547}
1548
1549#[salsa::interned(unsafe(no_lifetime), debug, revisions = usize::MAX)]
1555#[doc(alias = "MacroFileId")]
1556pub struct MacroCallId {
1557 #[returns(ref)]
1558 pub loc: MacroCallLoc,
1559}
1560
1561impl From<span::MacroCallId> for MacroCallId {
1562 #[inline]
1563 fn from(value: span::MacroCallId) -> Self {
1564 MacroCallId::from_id(value.0)
1565 }
1566}
1567
1568impl From<MacroCallId> for span::MacroCallId {
1569 #[inline]
1570 fn from(value: MacroCallId) -> span::MacroCallId {
1571 span::MacroCallId(value.as_id())
1572 }
1573}
1574
1575#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
1576pub enum HirFileId {
1577 FileId(EditionedFileId),
1578 MacroFile(MacroCallId),
1579}
1580
1581impl From<EditionedFileId> for HirFileId {
1582 #[inline]
1583 fn from(file_id: EditionedFileId) -> Self {
1584 HirFileId::FileId(file_id)
1585 }
1586}
1587
1588impl From<MacroCallId> for HirFileId {
1589 #[inline]
1590 fn from(file_id: MacroCallId) -> Self {
1591 HirFileId::MacroFile(file_id)
1592 }
1593}
1594
1595impl PartialEq<EditionedFileId> for HirFileId {
1596 fn eq(&self, &other: &EditionedFileId) -> bool {
1597 *self == HirFileId::from(other)
1598 }
1599}
1600impl PartialEq<HirFileId> for EditionedFileId {
1601 fn eq(&self, &other: &HirFileId) -> bool {
1602 other == HirFileId::from(*self)
1603 }
1604}
1605
1606impl HirFileId {
1607 #[inline]
1608 pub fn macro_file(self) -> Option<MacroCallId> {
1609 match self {
1610 HirFileId::FileId(_) => None,
1611 HirFileId::MacroFile(it) => Some(it),
1612 }
1613 }
1614
1615 #[inline]
1616 pub fn is_macro(self) -> bool {
1617 matches!(self, HirFileId::MacroFile(_))
1618 }
1619
1620 #[inline]
1621 pub fn file_id(self) -> Option<EditionedFileId> {
1622 match self {
1623 HirFileId::FileId(it) => Some(it),
1624 HirFileId::MacroFile(_) => None,
1625 }
1626 }
1627
1628 pub fn syntax_context(self, db: &dyn SourceDatabase, edition: Edition) -> SyntaxContext {
1629 match self {
1630 HirFileId::FileId(_) => SyntaxContext::root(edition),
1631 HirFileId::MacroFile(m) => {
1632 let kind = &m.loc(db).kind;
1633 m.macro_arg_considering_derives(db, kind).2.ctx
1634 }
1635 }
1636 }
1637
1638 pub fn edition(self, db: &dyn SourceDatabase) -> Edition {
1639 match self {
1640 HirFileId::FileId(file_id) => file_id.edition(db),
1641 HirFileId::MacroFile(m) => m.loc(db).def.edition,
1642 }
1643 }
1644
1645 pub fn original_file(self, db: &dyn SourceDatabase) -> EditionedFileId {
1646 let mut file_id = self;
1647 loop {
1648 match file_id {
1649 HirFileId::FileId(id) => break id,
1650 HirFileId::MacroFile(macro_call_id) => {
1651 file_id = macro_call_id.loc(db).kind.file_id()
1652 }
1653 }
1654 }
1655 }
1656
1657 pub fn original_file_respecting_includes(mut self, db: &dyn SourceDatabase) -> EditionedFileId {
1658 loop {
1659 match self {
1660 HirFileId::FileId(id) => break id,
1661 HirFileId::MacroFile(file) => {
1662 let loc = file.loc(db);
1663 if loc.def.is_include()
1664 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind
1665 && let Ok(it) = include_input_to_file_id(db, file, &eager.arg)
1666 {
1667 break it;
1668 }
1669 self = loc.kind.file_id();
1670 }
1671 }
1672 }
1673 }
1674
1675 pub fn original_call_node(self, db: &dyn SourceDatabase) -> Option<InRealFile<SyntaxNode>> {
1676 let mut call = self.macro_file()?.loc(db).to_node(db);
1677 loop {
1678 match call.file_id {
1679 HirFileId::FileId(file_id) => {
1680 break Some(InRealFile { file_id, value: call.value });
1681 }
1682 HirFileId::MacroFile(macro_call_id) => {
1683 call = macro_call_id.loc(db).to_node(db);
1684 }
1685 }
1686 }
1687 }
1688
1689 pub fn call_node(self, db: &dyn SourceDatabase) -> Option<InFile<SyntaxNode>> {
1690 Some(self.macro_file()?.loc(db).to_node(db))
1691 }
1692
1693 pub fn as_builtin_derive_attr_node(
1694 &self,
1695 db: &dyn SourceDatabase,
1696 ) -> Option<InFile<ast::Attr>> {
1697 let macro_file = self.macro_file()?;
1698 let loc = macro_file.loc(db);
1699 let attr = match loc.def.kind {
1700 MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
1701 _ => return None,
1702 };
1703 Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
1704 }
1705
1706 pub fn parse_or_expand(self, db: &dyn SourceDatabase) -> SyntaxNode {
1709 match self {
1710 HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(),
1711 HirFileId::MacroFile(macro_file) => {
1712 macro_file.parse_macro_expansion(db).value.0.syntax_node()
1713 }
1714 }
1715 }
1716
1717 pub(crate) fn parse_with_map(
1718 self,
1719 db: &dyn SourceDatabase,
1720 ) -> (Parse<SyntaxNode>, SpanMap<'_>) {
1721 match self {
1722 HirFileId::FileId(file_id) => (
1723 file_id.parse(db).to_syntax(),
1724 SpanMap::RealSpanMap(crate::span_map::real_span_map(db, file_id)),
1725 ),
1726 HirFileId::MacroFile(macro_file) => {
1727 let (parse, map) = ¯o_file.parse_macro_expansion(db).value;
1728 (parse.clone(), SpanMap::ExpansionSpanMap(map))
1729 }
1730 }
1731 }
1732
1733 #[inline]
1744 pub fn macro_expansion_depth(self, db: &dyn SourceDatabase) -> u32 {
1745 match self {
1746 HirFileId::MacroFile(macro_call) => macro_call.loc(db).macro_depth,
1747 HirFileId::FileId(_) => 0,
1748 }
1749 }
1750}
1751
1752#[salsa::tracked]
1753impl HirFileId {
1754 #[salsa::tracked(lru = 1024, returns(ref))]
1755 pub fn ast_id_map(self, db: &dyn SourceDatabase) -> AstIdMap {
1756 AstIdMap::from_source(&self.parse_or_expand(db))
1757 }
1758}