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}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247pub struct MacroDefId {
248 pub krate: Crate,
249 pub edition: Edition,
250 pub kind: MacroDefKind,
251 pub local_inner: bool,
252 pub allow_internal_unsafe: bool,
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
256pub enum MacroDefKind {
257 Declarative(AstId<ast::Macro>, MacroCallStyles),
258 BuiltIn(AstId<ast::Macro>, BuiltinFnLikeExpander),
259 BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander),
260 BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander),
261 BuiltInEager(AstId<ast::Macro>, EagerExpander),
262 UnimplementedBuiltIn(AstId<ast::Macro>),
263 ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
264}
265
266impl MacroDefKind {
267 #[inline]
268 pub fn is_declarative(&self) -> bool {
269 matches!(self, MacroDefKind::Declarative(..))
270 }
271
272 pub fn erased_ast_id(&self) -> ErasedAstId {
273 match *self {
274 MacroDefKind::ProcMacro(id, ..) => id.erase(),
275 MacroDefKind::BuiltIn(id, _)
276 | MacroDefKind::BuiltInAttr(id, _)
277 | MacroDefKind::BuiltInDerive(id, _)
278 | MacroDefKind::BuiltInEager(id, _)
279 | MacroDefKind::Declarative(id, ..)
280 | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(),
281 }
282 }
283}
284
285#[derive(Debug, Clone, PartialEq, Eq, Hash)]
286pub struct EagerCallInfo {
287 arg: tt::TopSubtree,
289 arg_id: MacroCallId,
291 error: Option<ExpandError>,
292 span: Span,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, Hash)]
297pub enum MacroCallKind {
298 FnLike {
299 ast_id: AstId<ast::MacroCall>,
300 expand_to: ExpandTo,
301 eager: Option<Box<EagerCallInfo>>,
306 },
307 Derive {
308 ast_id: AstId<ast::Adt>,
309 derive_attr_index: AttrId,
311 derive_index: u32,
313 derive_macro_id: MacroCallId,
316 },
317 Attr {
318 ast_id: AstId<ast::Item>,
319 attr_args: Option<Box<tt::TopSubtree>>,
321 censored_attr_ids: AttrMacroAttrIds,
329 },
330}
331
332#[derive(Debug, Clone, PartialEq, Eq, Hash)]
333pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr);
334
335impl AttrMacroAttrIds {
336 #[inline]
337 pub fn from_one(id: AttrId) -> Self {
338 Self(AttrMacroAttrIdsRepr::One(id))
339 }
340
341 #[inline]
342 pub fn from_many(ids: &[AttrId]) -> Self {
343 if let &[id] = ids {
344 Self(AttrMacroAttrIdsRepr::One(id))
345 } else {
346 Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect()))
347 }
348 }
349}
350
351#[derive(Debug, Clone, PartialEq, Eq, Hash)]
352enum AttrMacroAttrIdsRepr {
353 One(AttrId),
354 ManyDerives(ThinVec<AttrId>),
355}
356
357impl ops::Deref for AttrMacroAttrIds {
358 type Target = [AttrId];
359
360 #[inline]
361 fn deref(&self) -> &Self::Target {
362 match &self.0 {
363 AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one),
364 AttrMacroAttrIdsRepr::ManyDerives(many) => many,
365 }
366 }
367}
368
369impl AttrMacroAttrIds {
370 #[inline]
371 pub fn invoc_attr(&self) -> AttrId {
372 match &self.0 {
373 AttrMacroAttrIdsRepr::One(it) => *it,
374 AttrMacroAttrIdsRepr::ManyDerives(it) => {
375 *it.last().expect("should always have at least one `AttrId`")
376 }
377 }
378 }
379}
380
381impl MacroCallKind {
382 pub(crate) fn call_style(&self) -> MacroCallStyle {
383 match self {
384 MacroCallKind::FnLike { .. } => MacroCallStyle::FnLike,
385 MacroCallKind::Derive { .. } => MacroCallStyle::Derive,
386 MacroCallKind::Attr { .. } => MacroCallStyle::Attr,
387 }
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
392pub enum MacroKind {
393 Declarative,
395 DeclarativeBuiltIn,
397 Derive,
399 DeriveBuiltIn,
401 Attr,
403 AttrBuiltIn,
405 ProcMacro,
407}
408
409impl MacroCallId {
410 pub fn call_node(self, db: &dyn SourceDatabase) -> InFile<SyntaxNode> {
411 self.loc(db).to_node(db)
412 }
413 pub fn expansion_level(self, db: &dyn SourceDatabase) -> u32 {
414 let mut level = 0;
415 let mut macro_file = self;
416 loop {
417 let loc = macro_file.loc(db);
418
419 level += 1;
420 macro_file = match loc.kind.file_id() {
421 HirFileId::FileId(_) => break level,
422 HirFileId::MacroFile(it) => it,
423 };
424 }
425 }
426 pub fn parent(self, db: &dyn SourceDatabase) -> HirFileId {
427 self.loc(db).kind.file_id()
428 }
429
430 pub fn expansion_info(self, db: &dyn SourceDatabase) -> ExpansionInfo<'_> {
432 ExpansionInfo::new(db, self)
433 }
434
435 pub fn kind(self, db: &dyn SourceDatabase) -> MacroKind {
436 match self.loc(db).def.kind {
437 MacroDefKind::Declarative(..) => MacroKind::Declarative,
438 MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
439 MacroKind::DeclarativeBuiltIn
440 }
441 MacroDefKind::BuiltInDerive(..) => MacroKind::DeriveBuiltIn,
442 MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => MacroKind::Derive,
443 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr,
444 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro,
445 MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn,
446 MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative,
447 }
448 }
449
450 pub fn is_include_macro(self, db: &dyn SourceDatabase) -> bool {
451 self.loc(db).def.is_include()
452 }
453
454 pub fn is_include_like_macro(self, db: &dyn SourceDatabase) -> bool {
455 self.loc(db).def.is_include_like()
456 }
457
458 pub fn is_env_or_option_env(self, db: &dyn SourceDatabase) -> bool {
459 self.loc(db).def.is_env_or_option_env()
460 }
461
462 pub fn is_eager(self, db: &dyn SourceDatabase) -> bool {
463 let loc = self.loc(db);
464 matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
465 }
466
467 pub fn eager_arg(self, db: &dyn SourceDatabase) -> Option<MacroCallId> {
468 let loc = self.loc(db);
469 match &loc.kind {
470 MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
471 _ => None,
472 }
473 }
474
475 pub fn is_derive_attr_pseudo_expansion(self, db: &dyn SourceDatabase) -> bool {
476 let loc = self.loc(db);
477 loc.def.is_attribute_derive()
478 }
479}
480
481#[salsa::tracked]
482impl MacroCallId {
483 #[salsa::tracked(returns(ref), lru = 512)]
487 pub fn parse_macro_expansion(
488 self,
489 db: &dyn SourceDatabase,
490 ) -> ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)> {
491 let _p = tracing::info_span!("parse_macro_expansion").entered();
492 let loc = self.loc(db);
493 let expand_to = loc.expand_to();
494 let mbe::ValueResult { value: (tt, matched_arm), err } = self.macro_expand(db, loc);
495
496 let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to);
497 rev_token_map.matched_arm = matched_arm;
498
499 ExpandResult { value: (parse, rev_token_map), err }
500 }
501
502 pub fn parse_macro_expansion_error(
503 self,
504 db: &dyn SourceDatabase,
505 ) -> Option<ExpandResult<Arc<[SyntaxError]>>> {
506 let e: ExpandResult<Arc<[SyntaxError]>> =
507 self.parse_macro_expansion(db).as_ref().map(|it| Arc::from(it.0.errors()));
508 if e.value.is_empty() && e.err.is_none() { None } else { Some(e) }
509 }
510
511 #[allow(deprecated)] pub fn macro_arg_considering_derives<'db>(
519 self,
520 db: &'db dyn SourceDatabase,
521 kind: &MacroCallKind,
522 ) -> &'db MacroArgResult {
523 match kind {
524 MacroCallKind::Derive { derive_macro_id, .. } => derive_macro_id.macro_arg(db),
526 _ => self.macro_arg(db),
528 }
529 }
530
531 #[salsa::tracked(returns(ref))]
535 fn macro_arg(self, db: &dyn SourceDatabase) -> MacroArgResult {
536 let loc = self.loc(db);
537
538 if let MacroCallLoc {
539 def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
540 kind: MacroCallKind::FnLike { eager: Some(eager), .. },
541 ..
542 } = &loc
543 {
544 return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span);
545 }
546
547 let (parse, map) = loc.kind.file_id().parse_with_map(db);
548 let root = parse.syntax_node();
549
550 let (is_derive, censor_item_tree_attr_ids, item_node, span) = match &loc.kind {
551 MacroCallKind::FnLike { ast_id, .. } => {
552 let node = &ast_id.to_ptr(db).to_node(&root);
553 let path_range = node
554 .path()
555 .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range());
556 let span = map.span_for_range(path_range);
557
558 let dummy_tt = |kind| {
559 (
560 tt::TopSubtree::from_token_trees(
561 tt::Delimiter { open: span, close: span, kind },
562 tt::TokenTreesView::empty(),
563 ),
564 SyntaxFixupUndoInfo::default(),
565 span,
566 )
567 };
568
569 let Some(tt) = node.token_tree() else {
570 return dummy_tt(tt::DelimiterKind::Invisible);
571 };
572 let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
573 let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
574
575 let mismatched_delimiters = !matches!(
576 (first, last),
577 (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
578 );
579 if mismatched_delimiters {
580 cov_mark::hit!(issue9358_bad_macro_stack_overflow);
587
588 let kind = match first {
589 _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
590 T!['('] => tt::DelimiterKind::Parenthesis,
591 T!['['] => tt::DelimiterKind::Bracket,
592 T!['{'] => tt::DelimiterKind::Brace,
593 _ => tt::DelimiterKind::Invisible,
594 };
595 return dummy_tt(kind);
596 }
597
598 let mut tt = syntax_bridge::syntax_node_to_token_tree(
599 tt.syntax(),
600 map,
601 span,
602 if loc.def.is_proc_macro() {
603 DocCommentDesugarMode::ProcMacro
604 } else {
605 DocCommentDesugarMode::Mbe
606 },
607 );
608 if loc.def.is_proc_macro() {
609 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
611 }
612 return (tt, SyntaxFixupUndoInfo::NONE, span);
613 }
614 MacroCallKind::Derive { .. } => {
616 unreachable!("`MacroCallId::macro_arg` called with `MacroCallKind::Derive`")
617 }
618 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
619 let node = ast_id.to_ptr(db).to_node(&root);
620 let (_, attr) =
621 attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node);
622 let range = attr
623 .path()
624 .map(|path| path.syntax().text_range())
625 .unwrap_or_else(|| attr.syntax().text_range());
626 let span = map.span_for_range(range);
627
628 let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
629 (is_derive, &**attr_ids, node, span)
630 }
631 };
632
633 let (mut tt, undo_info) = attr_macro_input_to_token_tree(
634 db,
635 item_node.syntax(),
636 map,
637 span,
638 is_derive,
639 censor_item_tree_attr_ids,
640 loc.krate,
641 );
642
643 if loc.def.is_proc_macro() {
644 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
646 }
647
648 (tt, undo_info, span)
649 }
650
651 fn macro_expand<'db>(
652 self,
653 db: &'db dyn SourceDatabase,
654 loc: &MacroCallLoc,
655 ) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> {
656 let _p = tracing::info_span!("macro_expand").entered();
657
658 let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
659 MacroDefKind::ProcMacro(..) => {
660 return self.expand_proc_macro(db).as_ref().map(|it| (Cow::Borrowed(it), None));
661 }
662 _ => {
663 let (macro_arg, undo_info, span) =
664 self.macro_arg_considering_derives(db, &loc.kind);
665 let span = *span;
666
667 let arg = macro_arg;
668 let res = match loc.def.kind {
669 MacroDefKind::Declarative(id, _) => {
670 id.decl_macro_expander(db, loc.def.krate).expand(db, arg, self, span)
671 }
672 MacroDefKind::BuiltIn(_, it) => {
673 it.expand(db, self, arg, span).map_err(Into::into).zip_val(None)
674 }
675 MacroDefKind::BuiltInDerive(_, it) => {
676 it.expand(db, self, arg, span).map_err(Into::into).zip_val(None)
677 }
678 MacroDefKind::UnimplementedBuiltIn(_) => {
679 expand_unimplemented_builtin_macro(span).zip_val(None)
680 }
681 MacroDefKind::BuiltInEager(_, it) => {
682 let eager = match &loc.kind {
689 MacroCallKind::FnLike { eager: None, .. } => {
690 return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None);
691 }
692 MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
693 _ => None,
694 };
695
696 let mut res = it.expand(db, self, arg, span).map_err(Into::into);
697
698 if let Some(EagerCallInfo { error, .. }) = eager {
699 res.err = error.clone().or(res.err);
701 }
702 res.zip_val(None)
703 }
704 MacroDefKind::BuiltInAttr(_, it) => {
705 let mut res = it.expand(db, self, arg, span);
706 fixup::reverse_fixups(&mut res.value, undo_info);
707 res.zip_val(None)
708 }
709 MacroDefKind::ProcMacro(_, _, _) => unreachable!(),
710 };
711 (res, span)
712 }
713 };
714
715 if !loc.def.is_include() {
717 if let Err(value) = check_tt_count(&tt) {
719 return value
720 .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))))
721 .zip_val(matched_arm);
722 }
723 }
724
725 ExpandResult { value: (Cow::Owned(tt), matched_arm), err }
726 }
727
728 #[salsa::tracked(returns(ref))]
733 fn expand_proc_macro(self, db: &dyn SourceDatabase) -> ExpandResult<tt::TopSubtree> {
734 let loc = self.loc(db);
735 let (macro_arg, undo_info, span) = self.macro_arg_considering_derives(db, &loc.kind);
736
737 let (ast, expander) = match loc.def.kind {
738 MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander),
739 _ => unreachable!(),
740 };
741
742 let attr_arg = match &loc.kind {
743 MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args),
744 _ => None,
745 };
746
747 let ExpandResult { value: mut tt, err } = {
748 let span = proc_macro_span(db, ast);
749 expander.expand(
750 db,
751 loc.def.krate,
752 loc.krate,
753 macro_arg,
754 attr_arg,
755 span_with_def_site_ctxt(db, span, self.into(), loc.def.edition),
756 span_with_call_site_ctxt(db, span, self.into(), loc.def.edition),
757 span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition),
758 )
759 };
760
761 if let Err(value) = check_tt_count(&tt) {
763 return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span)));
764 }
765
766 fixup::reverse_fixups(&mut tt, undo_info);
767
768 ExpandResult { value: tt, err }
769 }
770}
771
772impl MacroCallId {
773 pub fn expand_speculative(
778 self,
779 db: &dyn SourceDatabase,
780 speculative_args: &SyntaxNode,
781 token_to_map: SyntaxToken,
782 ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
783 let loc = self.loc(db);
784 let (_, _, span) = *self.macro_arg_considering_derives(db, &loc.kind);
785
786 let span_map = RealSpanMap::absolute(span.anchor.file_id);
787 let span_map = SpanMap::RealSpanMap(&span_map);
788
789 let (mut tt, undo_info) = match &loc.kind {
791 MacroCallKind::FnLike { .. } => (
792 syntax_bridge::syntax_node_to_token_tree(
793 speculative_args,
794 span_map,
795 span,
796 if loc.def.is_proc_macro() {
797 DocCommentDesugarMode::ProcMacro
798 } else {
799 DocCommentDesugarMode::Mbe
800 },
801 ),
802 SyntaxFixupUndoInfo::NONE,
803 ),
804 MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
805 syntax_bridge::syntax_node_to_token_tree(
806 speculative_args,
807 span_map,
808 span,
809 DocCommentDesugarMode::ProcMacro,
810 ),
811 SyntaxFixupUndoInfo::NONE,
812 ),
813 MacroCallKind::Derive { derive_macro_id, .. } => {
814 let MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } =
815 &derive_macro_id.loc(db).kind
816 else {
817 unreachable!("`derive_macro_id` should be `MacroCallKind::Attr`");
818 };
819 attr_macro_input_to_token_tree(
820 db,
821 speculative_args,
822 span_map,
823 span,
824 true,
825 attr_ids,
826 loc.krate,
827 )
828 }
829 MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
830 attr_macro_input_to_token_tree(
831 db,
832 speculative_args,
833 span_map,
834 span,
835 false,
836 attr_ids,
837 loc.krate,
838 )
839 }
840 };
841
842 let attr_arg = match &loc.kind {
843 MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
844 if loc.def.is_attribute_derive() {
845 ast::Attr::cast(speculative_args.clone())
847 .and_then(|attr| {
848 if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? {
849 meta.token_tree()
850 } else {
851 None
852 }
853 })
854 .map(|token_tree| {
855 let mut tree = syntax_node_to_token_tree(
856 token_tree.syntax(),
857 span_map,
858 span,
859 DocCommentDesugarMode::ProcMacro,
860 );
861 tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
862 tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
863 tree
864 })
865 } else {
866 let item = ast::Item::cast(speculative_args.clone())?;
869 let (_, meta) = attr_ids
870 .invoc_attr()
871 .find_attr_range_with_source_opt(db, loc.krate, &item)?;
872 if let ast::Meta::TokenTreeMeta(meta) = meta
873 && let Some(tt) = meta.token_tree()
874 {
875 let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
876 tt.syntax(),
877 span_map,
878 span,
879 DocCommentDesugarMode::ProcMacro,
880 );
881 attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
882 Some(attr_arg)
883 } else {
884 None
885 }
886 }
887 }
888 _ => None,
889 };
890
891 let mut speculative_expansion = match loc.def.kind {
894 MacroDefKind::ProcMacro(ast, expander, _) => {
895 let span = proc_macro_span(db, ast);
896 tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
897 tt.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
898 expander.expand(
899 db,
900 loc.def.krate,
901 loc.krate,
902 &tt,
903 attr_arg.as_ref(),
904 span_with_def_site_ctxt(db, span, self.into(), loc.def.edition),
905 span_with_call_site_ctxt(db, span, self.into(), loc.def.edition),
906 span_with_mixed_site_ctxt(db, span, self.into(), loc.def.edition),
907 )
908 }
909 MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
910 pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
911 }
912 MacroDefKind::Declarative(it, _) => it
913 .decl_macro_expander(db, loc.krate)
914 .expand_unhygienic(db, &tt, loc.kind.call_style(), span),
915 MacroDefKind::BuiltIn(_, it) => it.expand(db, self, &tt, span).map_err(Into::into),
916 MacroDefKind::BuiltInDerive(_, it) => {
917 it.expand(db, self, &tt, span).map_err(Into::into)
918 }
919 MacroDefKind::BuiltInEager(_, it) => it.expand(db, self, &tt, span).map_err(Into::into),
920 MacroDefKind::BuiltInAttr(_, it) => it.expand(db, self, &tt, span),
921 MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span),
922 };
923
924 let expand_to = loc.expand_to();
925
926 fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
927 let (node, rev_tmap) =
928 token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to);
929
930 let syntax_node = node.syntax_node();
931 let token = rev_tmap
932 .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
933 .filter_map(|(range, ctx)| {
934 syntax_node.covering_element(range).into_token().zip(Some(ctx))
935 })
936 .map(|(t, ctx)| {
937 let ranking = ctx.is_opaque(db) as u8
941 + 2 * (t.kind() != token_to_map.kind()) as u8
942 + 4 * ((t.text() != token_to_map.text()) as u8);
943 (t, ranking)
944 })
945 .collect();
946 Some((node.syntax_node(), token))
947 }
948}
949
950fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult<tt::TopSubtree> {
951 ExpandResult::new(
952 tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
953 ExpandError::other(span, "this built-in macro is not implemented"),
954 )
955}
956
957fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>) -> Span {
963 #[salsa::tracked]
964 fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>, _: ()) -> Span {
965 let (parse, span_map) = ast.file_id.parse_with_map(db);
966 let root = parse.syntax_node();
967 let ast_id_map = ast.file_id.ast_id_map(db);
968
969 let node = ast_id_map.get(ast.value).to_node(&root);
970 let range = ast::HasName::name(&node)
971 .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range());
972 span_map.span_for_range(range)
973 }
974 proc_macro_span(db, ast, ())
975}
976
977pub(crate) fn token_tree_to_syntax_node(
978 db: &dyn SourceDatabase,
979 tt: &tt::TopSubtree,
980 expand_to: ExpandTo,
981) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
982 let entry_point = match expand_to {
983 ExpandTo::Statements => syntax_bridge::TopEntryPoint::MacroStmts,
984 ExpandTo::Items => syntax_bridge::TopEntryPoint::MacroItems,
985 ExpandTo::Pattern => syntax_bridge::TopEntryPoint::Pattern,
986 ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
987 ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
988 };
989 syntax_bridge::token_tree_to_syntax_node(tt, entry_point, &mut |ctx| ctx.edition(db))
990}
991
992fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
993 let tt = tt.top_subtree();
994 let count = tt.count();
995 if count <= TOKEN_LIMIT {
996 Ok(())
997 } else {
998 Err(ExpandResult {
999 value: (),
1000 err: Some(ExpandError::other(
1001 tt.delimiter.open,
1002 format!(
1003 "macro invocation exceeds token limit: produced {count} tokens, limit is {TOKEN_LIMIT}",
1004 ),
1005 )),
1006 })
1007 }
1008}
1009
1010impl MacroDefId {
1011 pub fn make_call(
1012 self,
1013 db: &dyn SourceDatabase,
1014 krate: Crate,
1015 kind: MacroCallKind,
1016 ctxt: SyntaxContext,
1017 ) -> MacroCallId {
1018 MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt })
1019 }
1020
1021 pub fn definition_range(&self, db: &dyn SourceDatabase) -> InFile<TextRange> {
1022 match self.kind {
1023 MacroDefKind::Declarative(id, _)
1024 | MacroDefKind::BuiltIn(id, _)
1025 | MacroDefKind::BuiltInAttr(id, _)
1026 | MacroDefKind::BuiltInDerive(id, _)
1027 | MacroDefKind::BuiltInEager(id, _)
1028 | MacroDefKind::UnimplementedBuiltIn(id) => {
1029 id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range())
1030 }
1031 MacroDefKind::ProcMacro(id, _, _) => {
1032 id.with_value(id.file_id.ast_id_map(db).get(id.value).text_range())
1033 }
1034 }
1035 }
1036
1037 pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
1038 match self.kind {
1039 MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
1040 MacroDefKind::Declarative(id, _)
1041 | MacroDefKind::BuiltIn(id, _)
1042 | MacroDefKind::BuiltInAttr(id, _)
1043 | MacroDefKind::BuiltInDerive(id, _)
1044 | MacroDefKind::BuiltInEager(id, _)
1045 | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id),
1046 }
1047 }
1048
1049 pub fn is_proc_macro(&self) -> bool {
1050 matches!(self.kind, MacroDefKind::ProcMacro(..))
1051 }
1052
1053 pub fn is_attribute(&self) -> bool {
1054 match self.kind {
1055 MacroDefKind::BuiltInAttr(..)
1056 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
1057 | MacroDefKind::UnimplementedBuiltIn(_) => true,
1058 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR),
1059 _ => false,
1060 }
1061 }
1062
1063 pub fn is_derive(&self) -> bool {
1064 match self.kind {
1065 MacroDefKind::BuiltInDerive(..)
1066 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
1067 | MacroDefKind::UnimplementedBuiltIn(_) => true,
1068 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE),
1069 _ => false,
1070 }
1071 }
1072
1073 pub fn is_fn_like(&self) -> bool {
1074 matches!(
1075 self.kind,
1076 MacroDefKind::BuiltIn(..)
1077 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
1078 | MacroDefKind::BuiltInEager(..)
1079 | MacroDefKind::Declarative(..)
1080 | MacroDefKind::UnimplementedBuiltIn(_)
1081 )
1082 }
1083
1084 pub fn is_attribute_derive(&self) -> bool {
1085 matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
1086 }
1087
1088 pub fn is_include(&self) -> bool {
1089 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
1090 }
1091
1092 pub fn is_include_like(&self) -> bool {
1093 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
1094 }
1095
1096 pub fn is_env_or_option_env(&self) -> bool {
1097 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
1098 }
1099}
1100
1101impl MacroCallLoc {
1102 pub fn to_node(&self, db: &dyn SourceDatabase) -> InFile<SyntaxNode> {
1103 match &self.kind {
1104 MacroCallKind::FnLike { ast_id, .. } => {
1105 ast_id.with_value(ast_id.to_node(db).syntax().clone())
1106 }
1107 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
1108 let (_, attr) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
1109 ast_id.with_value(attr.syntax().clone())
1110 }
1111 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
1112 if self.def.is_attribute_derive() {
1113 let (_, attr) = attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
1114 ast_id.with_value(attr.syntax().clone())
1115 } else {
1116 ast_id.with_value(ast_id.to_node(db).syntax().clone())
1117 }
1118 }
1119 }
1120 }
1121
1122 pub fn to_node_item(&self, db: &dyn SourceDatabase) -> InFile<ast::Item> {
1123 match self.kind {
1124 MacroCallKind::FnLike { ast_id, .. } => {
1125 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
1126 }
1127 MacroCallKind::Derive { ast_id, .. } => {
1128 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
1129 }
1130 MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
1131 }
1132 }
1133
1134 fn expand_to(&self) -> ExpandTo {
1135 match self.kind {
1136 MacroCallKind::FnLike { expand_to, .. } => expand_to,
1137 MacroCallKind::Derive { .. } => ExpandTo::Items,
1138 MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
1139 MacroCallKind::Attr { .. } => {
1140 ExpandTo::Items
1142 }
1143 }
1144 }
1145
1146 pub fn include_file_id(
1147 &self,
1148 db: &dyn SourceDatabase,
1149 macro_call_id: MacroCallId,
1150 ) -> Option<EditionedFileId> {
1151 if self.def.is_include()
1152 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind
1153 && let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg)
1154 {
1155 return Some(it);
1156 }
1157
1158 None
1159 }
1160}
1161
1162impl MacroCallKind {
1163 pub fn descr(&self) -> &'static str {
1164 match self {
1165 MacroCallKind::FnLike { .. } => "macro call",
1166 MacroCallKind::Derive { .. } => "derive macro",
1167 MacroCallKind::Attr { .. } => "attribute macro",
1168 }
1169 }
1170
1171 pub fn file_id(&self) -> HirFileId {
1173 match *self {
1174 MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
1175 | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
1176 | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
1177 }
1178 }
1179
1180 pub fn erased_ast_id(&self) -> ErasedFileAstId {
1181 match *self {
1182 MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
1183 MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
1184 MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
1185 }
1186 }
1187
1188 pub fn original_call_range_with_input(&self, db: &dyn SourceDatabase) -> FileRange {
1195 let get_range = |kind: &_| match kind {
1196 MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(),
1197 MacroCallKind::Derive { ast_id, .. } => ast_id.erase(),
1198 MacroCallKind::Attr { ast_id, .. } => ast_id.erase(),
1199 };
1200
1201 let mut ast_id = get_range(self);
1202 let mut file_id = self.file_id();
1203 let file_id = loop {
1204 match file_id {
1205 HirFileId::MacroFile(file) => {
1206 let kind = &file.loc(db).kind;
1207 ast_id = get_range(kind);
1208 file_id = kind.file_id();
1209 }
1210 HirFileId::FileId(file_id) => break file_id,
1211 }
1212 };
1213
1214 FileRange { range: ast_id.to_ptr(db).text_range(), file_id }
1215 }
1216
1217 pub fn original_call_range(&self, db: &dyn SourceDatabase, krate: Crate) -> FileRange {
1223 let get_range = |kind: &_| match kind {
1224 MacroCallKind::FnLike { ast_id, .. } => {
1225 let node = ast_id.to_node(db);
1226 node.path()
1227 .unwrap()
1228 .syntax()
1229 .text_range()
1230 .cover(node.excl_token().unwrap().text_range())
1231 }
1232 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
1233 derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range()
1235 }
1236 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
1238 attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range()
1239 }
1240 };
1241
1242 let mut range = get_range(self);
1243 let mut file_id = self.file_id();
1244 let file_id = loop {
1245 match file_id {
1246 HirFileId::MacroFile(file) => {
1247 let kind = &file.loc(db).kind;
1248 range = get_range(kind);
1249 file_id = kind.file_id();
1250 }
1251 HirFileId::FileId(file_id) => break file_id,
1252 }
1253 };
1254
1255 FileRange { range, file_id }
1256 }
1257
1258 fn arg(&self, db: &dyn SourceDatabase) -> InFile<Option<SyntaxNode>> {
1259 match self {
1260 MacroCallKind::FnLike { ast_id, .. } => {
1261 ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
1262 }
1263 MacroCallKind::Derive { ast_id, .. } => {
1264 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
1265 }
1266 MacroCallKind::Attr { ast_id, .. } => {
1267 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
1268 }
1269 }
1270 }
1271}
1272
1273#[derive(Clone, Debug, PartialEq, Eq)]
1277pub struct ExpansionInfo<'db> {
1278 expanded: InMacroFile<SyntaxNode>,
1279 arg: InFile<Option<SyntaxNode>>,
1281 exp_map: &'db ExpansionSpanMap,
1282 arg_map: SpanMap<'db>,
1283 loc: &'db MacroCallLoc,
1284}
1285
1286impl<'db> ExpansionInfo<'db> {
1287 pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
1288 self.expanded.clone()
1289 }
1290
1291 pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
1292 self.arg.as_ref().map(|it| it.as_ref())
1293 }
1294
1295 pub fn call_file(&self) -> HirFileId {
1296 self.arg.file_id
1297 }
1298
1299 pub fn is_attr(&self) -> bool {
1300 matches!(
1301 self.loc.def.kind,
1302 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
1303 )
1304 }
1305
1306 pub fn map_range_down_exact(
1312 &self,
1313 span: Span,
1314 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
1315 if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
1316 return None;
1317 }
1318
1319 let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
1320 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
1321 });
1322
1323 Some(InMacroFile::new(self.expanded.file_id, tokens))
1324 }
1325
1326 pub fn map_range_down(
1331 &self,
1332 span: Span,
1333 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
1334 if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
1335 return None;
1336 }
1337
1338 let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
1339 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
1340 });
1341
1342 Some(InMacroFile::new(self.expanded.file_id, tokens))
1343 }
1344
1345 pub fn span_for_offset(
1347 &self,
1348 db: &dyn SourceDatabase,
1349 offset: TextSize,
1350 ) -> (FileRange, SyntaxContext) {
1351 debug_assert!(self.expanded.value.text_range().contains(offset));
1352 span_for_offset(db, self.exp_map, offset)
1353 }
1354
1355 pub fn map_node_range_up(
1357 &self,
1358 db: &dyn SourceDatabase,
1359 range: TextRange,
1360 ) -> Option<(FileRange, SyntaxContext)> {
1361 debug_assert!(self.expanded.value.text_range().contains_range(range));
1362 map_node_range_up(db, self.exp_map, range)
1363 }
1364
1365 pub fn map_range_up_once(
1370 &self,
1371 db: &dyn SourceDatabase,
1372 token: TextRange,
1373 ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
1374 debug_assert!(self.expanded.value.text_range().contains_range(token));
1375 let span = self.exp_map.span_at(token.start());
1376 match &self.arg_map {
1377 SpanMap::RealSpanMap(_) => {
1378 let range = resolve_span(db, span);
1379 InFile { file_id: range.file_id.into(), value: smallvec::smallvec![range.range] }
1380 }
1381 SpanMap::ExpansionSpanMap(arg_map) => {
1382 let Some(arg_node) = &self.arg.value else {
1383 return InFile::new(self.arg.file_id, smallvec::smallvec![]);
1384 };
1385 let arg_range = arg_node.text_range();
1386 InFile::new(
1387 self.arg.file_id,
1388 arg_map
1389 .ranges_with_span_exact(span)
1390 .map(|(range, _)| range)
1391 .filter(|range| range.intersect(arg_range).is_some())
1392 .collect(),
1393 )
1394 }
1395 }
1396 }
1397
1398 pub fn new(db: &'db dyn SourceDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> {
1399 let _p = tracing::info_span!("ExpansionInfo::new").entered();
1400 let loc = macro_file.loc(db);
1401
1402 let arg_tt = loc.kind.arg(db);
1403 let arg_map = arg_tt.file_id.span_map(db);
1404
1405 let (parse, exp_map) = ¯o_file.parse_macro_expansion(db).value;
1406 let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
1407
1408 ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
1409 }
1410}
1411
1412pub fn map_node_range_up_rooted(
1416 db: &dyn SourceDatabase,
1417 exp_map: &ExpansionSpanMap,
1418 range: TextRange,
1419) -> Option<FileRange> {
1420 let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
1421 let Span { range, anchor, ctx } = spans.next()?;
1422 let mut start = range.start();
1423 let mut end = range.end();
1424
1425 for span in spans {
1426 if span.anchor != anchor {
1427 return None;
1428 }
1429 start = start.min(span.range.start());
1430 end = end.max(span.range.end());
1431 }
1432 Some(resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx }))
1433}
1434
1435pub fn map_node_range_up(
1439 db: &dyn SourceDatabase,
1440 exp_map: &ExpansionSpanMap,
1441 range: TextRange,
1442) -> Option<(FileRange, SyntaxContext)> {
1443 let mut spans = exp_map.spans_for_range(range);
1444 let Span { range, anchor, ctx } = spans.next()?;
1445 let mut start = range.start();
1446 let mut end = range.end();
1447
1448 for span in spans {
1449 if span.anchor != anchor || span.ctx != ctx {
1450 return None;
1451 }
1452 start = start.min(span.range.start());
1453 end = end.max(span.range.end());
1454 }
1455 Some((resolve_span(db, Span { range: TextRange::new(start, end), anchor, ctx }), ctx))
1456}
1457
1458pub fn span_for_offset(
1460 db: &dyn SourceDatabase,
1461 exp_map: &ExpansionSpanMap,
1462 offset: TextSize,
1463) -> (FileRange, SyntaxContext) {
1464 let span = exp_map.span_at(offset);
1465 (resolve_span(db, span), span.ctx)
1466}
1467
1468pub fn resolve_span(db: &dyn SourceDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange {
1472 let file_id = EditionedFileId::from_span_file_id(db, anchor.file_id);
1473 let anchor_offset =
1474 HirFileId::from(file_id).ast_id_map(db).get_erased(anchor.ast_id).text_range().start();
1475 FileRange { file_id, range: range + anchor_offset }
1476}
1477
1478#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1494pub enum ExpandTo {
1495 Statements,
1496 Items,
1497 Pattern,
1498 Type,
1499 Expr,
1500}
1501
1502impl ExpandTo {
1503 pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1504 use syntax::SyntaxKind::*;
1505
1506 let syn = call.syntax();
1507
1508 let parent = match syn.parent() {
1509 Some(it) => it,
1510 None => return ExpandTo::Statements,
1511 };
1512
1513 if parent.kind() == MACRO_EXPR
1516 && parent
1517 .parent()
1518 .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1519 {
1520 return ExpandTo::Statements;
1521 }
1522
1523 match parent.kind() {
1524 MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1525 MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1526 MACRO_PAT => ExpandTo::Pattern,
1527 MACRO_TYPE => ExpandTo::Type,
1528
1529 ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1530 | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1531 | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1532 | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1533 | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1534 _ => {
1535 ExpandTo::Items
1537 }
1538 }
1539 }
1540}
1541
1542#[salsa::interned(no_lifetime, debug, revisions = usize::MAX)]
1548#[doc(alias = "MacroFileId")]
1549pub struct MacroCallId {
1550 #[returns(ref)]
1551 pub loc: MacroCallLoc,
1552}
1553
1554impl From<span::MacroCallId> for MacroCallId {
1555 #[inline]
1556 fn from(value: span::MacroCallId) -> Self {
1557 MacroCallId::from_id(value.0)
1558 }
1559}
1560
1561impl From<MacroCallId> for span::MacroCallId {
1562 #[inline]
1563 fn from(value: MacroCallId) -> span::MacroCallId {
1564 span::MacroCallId(value.as_id())
1565 }
1566}
1567
1568#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
1569pub enum HirFileId {
1570 FileId(EditionedFileId),
1571 MacroFile(MacroCallId),
1572}
1573
1574impl From<EditionedFileId> for HirFileId {
1575 #[inline]
1576 fn from(file_id: EditionedFileId) -> Self {
1577 HirFileId::FileId(file_id)
1578 }
1579}
1580
1581impl From<MacroCallId> for HirFileId {
1582 #[inline]
1583 fn from(file_id: MacroCallId) -> Self {
1584 HirFileId::MacroFile(file_id)
1585 }
1586}
1587
1588impl PartialEq<EditionedFileId> for HirFileId {
1589 fn eq(&self, &other: &EditionedFileId) -> bool {
1590 *self == HirFileId::from(other)
1591 }
1592}
1593impl PartialEq<HirFileId> for EditionedFileId {
1594 fn eq(&self, &other: &HirFileId) -> bool {
1595 other == HirFileId::from(*self)
1596 }
1597}
1598
1599impl HirFileId {
1600 #[inline]
1601 pub fn macro_file(self) -> Option<MacroCallId> {
1602 match self {
1603 HirFileId::FileId(_) => None,
1604 HirFileId::MacroFile(it) => Some(it),
1605 }
1606 }
1607
1608 #[inline]
1609 pub fn is_macro(self) -> bool {
1610 matches!(self, HirFileId::MacroFile(_))
1611 }
1612
1613 #[inline]
1614 pub fn file_id(self) -> Option<EditionedFileId> {
1615 match self {
1616 HirFileId::FileId(it) => Some(it),
1617 HirFileId::MacroFile(_) => None,
1618 }
1619 }
1620
1621 pub fn syntax_context(self, db: &dyn SourceDatabase, edition: Edition) -> SyntaxContext {
1622 match self {
1623 HirFileId::FileId(_) => SyntaxContext::root(edition),
1624 HirFileId::MacroFile(m) => {
1625 let kind = &m.loc(db).kind;
1626 m.macro_arg_considering_derives(db, kind).2.ctx
1627 }
1628 }
1629 }
1630
1631 pub fn edition(self, db: &dyn SourceDatabase) -> Edition {
1632 match self {
1633 HirFileId::FileId(file_id) => file_id.edition(db),
1634 HirFileId::MacroFile(m) => m.loc(db).def.edition,
1635 }
1636 }
1637
1638 pub fn original_file(self, db: &dyn SourceDatabase) -> EditionedFileId {
1639 let mut file_id = self;
1640 loop {
1641 match file_id {
1642 HirFileId::FileId(id) => break id,
1643 HirFileId::MacroFile(macro_call_id) => {
1644 file_id = macro_call_id.loc(db).kind.file_id()
1645 }
1646 }
1647 }
1648 }
1649
1650 pub fn original_file_respecting_includes(mut self, db: &dyn SourceDatabase) -> EditionedFileId {
1651 loop {
1652 match self {
1653 HirFileId::FileId(id) => break id,
1654 HirFileId::MacroFile(file) => {
1655 let loc = file.loc(db);
1656 if loc.def.is_include()
1657 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind
1658 && let Ok(it) = include_input_to_file_id(db, file, &eager.arg)
1659 {
1660 break it;
1661 }
1662 self = loc.kind.file_id();
1663 }
1664 }
1665 }
1666 }
1667
1668 pub fn original_call_node(self, db: &dyn SourceDatabase) -> Option<InRealFile<SyntaxNode>> {
1669 let mut call = self.macro_file()?.loc(db).to_node(db);
1670 loop {
1671 match call.file_id {
1672 HirFileId::FileId(file_id) => {
1673 break Some(InRealFile { file_id, value: call.value });
1674 }
1675 HirFileId::MacroFile(macro_call_id) => {
1676 call = macro_call_id.loc(db).to_node(db);
1677 }
1678 }
1679 }
1680 }
1681
1682 pub fn call_node(self, db: &dyn SourceDatabase) -> Option<InFile<SyntaxNode>> {
1683 Some(self.macro_file()?.loc(db).to_node(db))
1684 }
1685
1686 pub fn as_builtin_derive_attr_node(
1687 &self,
1688 db: &dyn SourceDatabase,
1689 ) -> Option<InFile<ast::Attr>> {
1690 let macro_file = self.macro_file()?;
1691 let loc = macro_file.loc(db);
1692 let attr = match loc.def.kind {
1693 MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
1694 _ => return None,
1695 };
1696 Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
1697 }
1698
1699 pub fn parse_or_expand(self, db: &dyn SourceDatabase) -> SyntaxNode {
1702 match self {
1703 HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(),
1704 HirFileId::MacroFile(macro_file) => {
1705 macro_file.parse_macro_expansion(db).value.0.syntax_node()
1706 }
1707 }
1708 }
1709
1710 pub(crate) fn parse_with_map(
1711 self,
1712 db: &dyn SourceDatabase,
1713 ) -> (Parse<SyntaxNode>, SpanMap<'_>) {
1714 match self {
1715 HirFileId::FileId(file_id) => (
1716 file_id.parse(db).to_syntax(),
1717 SpanMap::RealSpanMap(crate::span_map::real_span_map(db, file_id)),
1718 ),
1719 HirFileId::MacroFile(macro_file) => {
1720 let (parse, map) = ¯o_file.parse_macro_expansion(db).value;
1721 (parse.clone(), SpanMap::ExpansionSpanMap(map))
1722 }
1723 }
1724 }
1725}
1726
1727#[salsa::tracked]
1728impl HirFileId {
1729 #[salsa::tracked(lru = 1024, returns(ref))]
1730 pub fn ast_id_map(self, db: &dyn SourceDatabase) -> AstIdMap {
1731 AstIdMap::from_source(&self.parse_or_expand(db))
1732 }
1733}