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 db;
16pub mod declarative;
17pub mod eager;
18pub mod files;
19pub mod hygiene;
20pub mod inert_attr_macro;
21pub mod mod_path;
22pub mod name;
23pub mod proc_macro;
24pub mod span_map;
25
26mod cfg_process;
27mod fixup;
28mod prettify_macro_expansion_;
29
30use salsa::plumbing::{AsId, FromId};
31use stdx::TupleExt;
32use thin_vec::ThinVec;
33use triomphe::Arc;
34
35use core::fmt;
36use std::{hash::Hash, ops};
37
38use base_db::Crate;
39use either::Either;
40use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContext};
41use syntax::{
42 SyntaxNode, SyntaxToken, TextRange, TextSize,
43 ast::{self, AstNode},
44};
45
46use crate::{
47 attrs::AttrId,
48 builtin::{
49 BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander,
50 include_input_to_file_id,
51 },
52 db::ExpandDatabase,
53 mod_path::ModPath,
54 proc_macro::{CustomProcMacroExpander, ProcMacroKind},
55 span_map::{ExpansionSpanMap, SpanMap},
56};
57
58pub use crate::{
59 cfg_process::check_cfg_attr_value,
60 files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
61 prettify_macro_expansion_::prettify_macro_expansion,
62};
63
64pub use base_db::EditionedFileId;
65pub use mbe::{DeclarativeMacro, MacroCallStyle, MacroCallStyles, ValueResult};
66
67pub mod tt {
68 pub use span::Span;
69 pub use tt::{DelimiterKind, IdentIsRaw, LitKind, Spacing, token_to_literal};
70
71 pub type Delimiter = ::tt::Delimiter<Span>;
72 pub type DelimSpan = ::tt::DelimSpan<Span>;
73 pub type Subtree = ::tt::Subtree<Span>;
74 pub type Leaf = ::tt::Leaf<Span>;
75 pub type Literal = ::tt::Literal<Span>;
76 pub type Punct = ::tt::Punct<Span>;
77 pub type Ident = ::tt::Ident<Span>;
78 pub type TokenTree = ::tt::TokenTree<Span>;
79 pub type TopSubtree = ::tt::TopSubtree<Span>;
80 pub type TopSubtreeBuilder = ::tt::TopSubtreeBuilder<Span>;
81 pub type TokenTreesView<'a> = ::tt::TokenTreesView<'a, Span>;
82 pub type SubtreeView<'a> = ::tt::SubtreeView<'a, Span>;
83 pub type TtElement<'a> = ::tt::iter::TtElement<'a, Span>;
84 pub type TtIter<'a> = ::tt::iter::TtIter<'a, Span>;
85}
86
87#[macro_export]
88macro_rules! impl_intern_lookup {
89 ($db:ident, $id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
90 impl $crate::Intern for $loc {
91 type Database = dyn $db;
92 type ID = $id;
93 fn intern(self, db: &Self::Database) -> Self::ID {
94 db.$intern(self)
95 }
96 }
97
98 impl $crate::Lookup for $id {
99 type Database = dyn $db;
100 type Data = $loc;
101 fn lookup(&self, db: &Self::Database) -> Self::Data {
102 db.$lookup(*self)
103 }
104 }
105 };
106}
107
108pub trait Intern {
110 type Database: ?Sized;
111 type ID;
112 fn intern(self, db: &Self::Database) -> Self::ID;
113}
114
115pub trait Lookup {
116 type Database: ?Sized;
117 type Data;
118 fn lookup(&self, db: &Self::Database) -> Self::Data;
119}
120
121impl_intern_lookup!(
122 ExpandDatabase,
123 MacroCallId,
124 MacroCallLoc,
125 intern_macro_call,
126 lookup_intern_macro_call
127);
128
129pub type ExpandResult<T> = ValueResult<T, ExpandError>;
130
131#[derive(Debug, PartialEq, Eq, Clone, Hash)]
132pub struct ExpandError {
133 inner: Arc<(ExpandErrorKind, Span)>,
134}
135
136impl ExpandError {
137 pub fn new(span: Span, kind: ExpandErrorKind) -> Self {
138 ExpandError { inner: Arc::new((kind, span)) }
139 }
140 pub fn other(span: Span, msg: impl Into<Box<str>>) -> Self {
141 ExpandError { inner: Arc::new((ExpandErrorKind::Other(msg.into()), span)) }
142 }
143 pub fn kind(&self) -> &ExpandErrorKind {
144 &self.inner.0
145 }
146 pub fn span(&self) -> Span {
147 self.inner.1
148 }
149
150 pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
151 self.inner.0.render_to_string(db)
152 }
153}
154
155#[derive(Debug, PartialEq, Eq, Clone, Hash)]
156pub enum ExpandErrorKind {
157 ProcMacroAttrExpansionDisabled,
159 MissingProcMacroExpander(Crate),
160 MacroDisabled,
162 MacroDefinition,
164 Mbe(mbe::ExpandErrorKind),
165 RecursionOverflow,
166 Other(Box<str>),
167 ProcMacroPanic(Box<str>),
168}
169
170pub struct RenderedExpandError {
171 pub message: String,
172 pub error: bool,
173 pub kind: &'static str,
174}
175
176impl fmt::Display for RenderedExpandError {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 write!(f, "{}", self.message)
179 }
180}
181
182impl RenderedExpandError {
183 const GENERAL_KIND: &str = "macro-error";
184 const DISABLED: &str = "proc-macro-disabled";
185 const ATTR_EXP_DISABLED: &str = "attribute-expansion-disabled";
186}
187
188impl ExpandErrorKind {
189 pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
190 match self {
191 ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
192 message: "procedural attribute macro expansion is disabled".to_owned(),
193 error: false,
194 kind: RenderedExpandError::ATTR_EXP_DISABLED,
195 },
196 ExpandErrorKind::MacroDisabled => RenderedExpandError {
197 message: "proc-macro is explicitly disabled".to_owned(),
198 error: false,
199 kind: RenderedExpandError::DISABLED,
200 },
201 &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
202 match db.proc_macros_for_crate(def_crate).as_ref().and_then(|it| it.get_error()) {
203 Some(e) => RenderedExpandError {
204 message: e.to_string(),
205 error: e.is_hard_error(),
206 kind: RenderedExpandError::GENERAL_KIND,
207 },
208 None => RenderedExpandError {
209 message: format!(
210 "internal error: proc-macro map is missing error entry for crate {def_crate:?}"
211 ),
212 error: true,
213 kind: RenderedExpandError::GENERAL_KIND,
214 },
215 }
216 }
217 ExpandErrorKind::MacroDefinition => RenderedExpandError {
218 message: "macro definition has parse errors".to_owned(),
219 error: true,
220 kind: RenderedExpandError::GENERAL_KIND,
221 },
222 ExpandErrorKind::Mbe(e) => RenderedExpandError {
223 message: e.to_string(),
224 error: true,
225 kind: RenderedExpandError::GENERAL_KIND,
226 },
227 ExpandErrorKind::RecursionOverflow => RenderedExpandError {
228 message: "overflow expanding the original macro".to_owned(),
229 error: true,
230 kind: RenderedExpandError::GENERAL_KIND,
231 },
232 ExpandErrorKind::Other(e) => RenderedExpandError {
233 message: (**e).to_owned(),
234 error: true,
235 kind: RenderedExpandError::GENERAL_KIND,
236 },
237 ExpandErrorKind::ProcMacroPanic(e) => RenderedExpandError {
238 message: format!("proc-macro panicked: {e}"),
239 error: true,
240 kind: RenderedExpandError::GENERAL_KIND,
241 },
242 }
243 }
244}
245
246impl From<mbe::ExpandError> for ExpandError {
247 fn from(mbe: mbe::ExpandError) -> Self {
248 ExpandError { inner: Arc::new((ExpandErrorKind::Mbe(mbe.inner.1.clone()), mbe.inner.0)) }
249 }
250}
251#[derive(Debug, Clone, PartialEq, Eq, Hash)]
252pub struct MacroCallLoc {
253 pub def: MacroDefId,
254 pub krate: Crate,
255 pub kind: MacroCallKind,
256 pub ctxt: SyntaxContext,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
260pub struct MacroDefId {
261 pub krate: Crate,
262 pub edition: Edition,
263 pub kind: MacroDefKind,
264 pub local_inner: bool,
265 pub allow_internal_unsafe: bool,
266}
267
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
269pub enum MacroDefKind {
270 Declarative(AstId<ast::Macro>, MacroCallStyles),
271 BuiltIn(AstId<ast::Macro>, BuiltinFnLikeExpander),
272 BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander),
273 BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander),
274 BuiltInEager(AstId<ast::Macro>, EagerExpander),
275 ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
276}
277
278impl MacroDefKind {
279 #[inline]
280 pub fn is_declarative(&self) -> bool {
281 matches!(self, MacroDefKind::Declarative(..))
282 }
283
284 pub fn erased_ast_id(&self) -> ErasedAstId {
285 match *self {
286 MacroDefKind::ProcMacro(id, ..) => id.erase(),
287 MacroDefKind::BuiltIn(id, _)
288 | MacroDefKind::BuiltInAttr(id, _)
289 | MacroDefKind::BuiltInDerive(id, _)
290 | MacroDefKind::BuiltInEager(id, _)
291 | MacroDefKind::Declarative(id, ..) => id.erase(),
292 }
293 }
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, Hash)]
297pub struct EagerCallInfo {
298 arg: Arc<tt::TopSubtree>,
300 arg_id: MacroCallId,
302 error: Option<ExpandError>,
303 span: Span,
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
308pub enum MacroCallKind {
309 FnLike {
310 ast_id: AstId<ast::MacroCall>,
311 expand_to: ExpandTo,
312 eager: Option<Arc<EagerCallInfo>>,
317 },
318 Derive {
319 ast_id: AstId<ast::Adt>,
320 derive_attr_index: AttrId,
322 derive_index: u32,
324 derive_macro_id: MacroCallId,
327 },
328 Attr {
329 ast_id: AstId<ast::Item>,
330 attr_args: Option<Arc<tt::TopSubtree>>,
332 censored_attr_ids: AttrMacroAttrIds,
340 },
341}
342
343#[derive(Debug, Clone, PartialEq, Eq, Hash)]
344pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr);
345
346impl AttrMacroAttrIds {
347 #[inline]
348 pub fn from_one(id: AttrId) -> Self {
349 Self(AttrMacroAttrIdsRepr::One(id))
350 }
351
352 #[inline]
353 pub fn from_many(ids: &[AttrId]) -> Self {
354 if let &[id] = ids {
355 Self(AttrMacroAttrIdsRepr::One(id))
356 } else {
357 Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect()))
358 }
359 }
360}
361
362#[derive(Debug, Clone, PartialEq, Eq, Hash)]
363enum AttrMacroAttrIdsRepr {
364 One(AttrId),
365 ManyDerives(ThinVec<AttrId>),
366}
367
368impl ops::Deref for AttrMacroAttrIds {
369 type Target = [AttrId];
370
371 #[inline]
372 fn deref(&self) -> &Self::Target {
373 match &self.0 {
374 AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one),
375 AttrMacroAttrIdsRepr::ManyDerives(many) => many,
376 }
377 }
378}
379
380impl AttrMacroAttrIds {
381 #[inline]
382 pub fn invoc_attr(&self) -> AttrId {
383 match &self.0 {
384 AttrMacroAttrIdsRepr::One(it) => *it,
385 AttrMacroAttrIdsRepr::ManyDerives(it) => {
386 *it.last().expect("should always have at least one `AttrId`")
387 }
388 }
389 }
390}
391
392impl MacroCallKind {
393 pub(crate) fn call_style(&self) -> MacroCallStyle {
394 match self {
395 MacroCallKind::FnLike { .. } => MacroCallStyle::FnLike,
396 MacroCallKind::Derive { .. } => MacroCallStyle::Derive,
397 MacroCallKind::Attr { .. } => MacroCallStyle::Attr,
398 }
399 }
400}
401
402impl HirFileId {
403 pub fn edition(self, db: &dyn ExpandDatabase) -> Edition {
404 match self {
405 HirFileId::FileId(file_id) => file_id.editioned_file_id(db).edition(),
406 HirFileId::MacroFile(m) => db.lookup_intern_macro_call(m).def.edition,
407 }
408 }
409 pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId {
410 let mut file_id = self;
411 loop {
412 match file_id {
413 HirFileId::FileId(id) => break id,
414 HirFileId::MacroFile(macro_call_id) => {
415 file_id = db.lookup_intern_macro_call(macro_call_id).kind.file_id()
416 }
417 }
418 }
419 }
420
421 pub fn original_file_respecting_includes(mut self, db: &dyn ExpandDatabase) -> EditionedFileId {
422 loop {
423 match self {
424 HirFileId::FileId(id) => break id,
425 HirFileId::MacroFile(file) => {
426 let loc = db.lookup_intern_macro_call(file);
427 if loc.def.is_include()
428 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind
429 && let Ok(it) = include_input_to_file_id(db, file, &eager.arg)
430 {
431 break it;
432 }
433 self = loc.kind.file_id();
434 }
435 }
436 }
437 }
438
439 pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>> {
440 let mut call = db.lookup_intern_macro_call(self.macro_file()?).to_node(db);
441 loop {
442 match call.file_id {
443 HirFileId::FileId(file_id) => {
444 break Some(InRealFile { file_id, value: call.value });
445 }
446 HirFileId::MacroFile(macro_call_id) => {
447 call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
448 }
449 }
450 }
451 }
452
453 pub fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>> {
454 Some(db.lookup_intern_macro_call(self.macro_file()?).to_node(db))
455 }
456
457 pub fn as_builtin_derive_attr_node(
458 &self,
459 db: &dyn ExpandDatabase,
460 ) -> Option<InFile<ast::Attr>> {
461 let macro_file = self.macro_file()?;
462 let loc = db.lookup_intern_macro_call(macro_file);
463 let attr = match loc.def.kind {
464 MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
465 _ => return None,
466 };
467 Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
468 }
469}
470
471#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
472pub enum MacroKind {
473 Declarative,
475 DeclarativeBuiltIn,
477 Derive,
479 DeriveBuiltIn,
481 Attr,
483 AttrBuiltIn,
485 ProcMacro,
487}
488
489impl MacroCallId {
490 pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
491 db.lookup_intern_macro_call(self).to_node(db)
492 }
493 pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 {
494 let mut level = 0;
495 let mut macro_file = self;
496 loop {
497 let loc = db.lookup_intern_macro_call(macro_file);
498
499 level += 1;
500 macro_file = match loc.kind.file_id() {
501 HirFileId::FileId(_) => break level,
502 HirFileId::MacroFile(it) => it,
503 };
504 }
505 }
506 pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId {
507 db.lookup_intern_macro_call(self).kind.file_id()
508 }
509
510 pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo {
512 ExpansionInfo::new(db, self)
513 }
514
515 pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind {
516 match db.lookup_intern_macro_call(self).def.kind {
517 MacroDefKind::Declarative(..) => MacroKind::Declarative,
518 MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
519 MacroKind::DeclarativeBuiltIn
520 }
521 MacroDefKind::BuiltInDerive(..) => MacroKind::DeriveBuiltIn,
522 MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => MacroKind::Derive,
523 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr,
524 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro,
525 MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn,
526 }
527 }
528
529 pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool {
530 db.lookup_intern_macro_call(self).def.is_include()
531 }
532
533 pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool {
534 db.lookup_intern_macro_call(self).def.is_include_like()
535 }
536
537 pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool {
538 db.lookup_intern_macro_call(self).def.is_env_or_option_env()
539 }
540
541 pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool {
542 let loc = db.lookup_intern_macro_call(self);
543 matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
544 }
545
546 pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
547 let loc = db.lookup_intern_macro_call(self);
548 match &loc.kind {
549 MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
550 _ => None,
551 }
552 }
553
554 pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool {
555 let loc = db.lookup_intern_macro_call(self);
556 loc.def.is_attribute_derive()
557 }
558}
559
560impl MacroDefId {
561 pub fn make_call(
562 self,
563 db: &dyn ExpandDatabase,
564 krate: Crate,
565 kind: MacroCallKind,
566 ctxt: SyntaxContext,
567 ) -> MacroCallId {
568 db.intern_macro_call(MacroCallLoc { def: self, krate, kind, ctxt })
569 }
570
571 pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
572 match self.kind {
573 MacroDefKind::Declarative(id, _)
574 | MacroDefKind::BuiltIn(id, _)
575 | MacroDefKind::BuiltInAttr(id, _)
576 | MacroDefKind::BuiltInDerive(id, _)
577 | MacroDefKind::BuiltInEager(id, _) => {
578 id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
579 }
580 MacroDefKind::ProcMacro(id, _, _) => {
581 id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
582 }
583 }
584 }
585
586 pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
587 match self.kind {
588 MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
589 MacroDefKind::Declarative(id, _)
590 | MacroDefKind::BuiltIn(id, _)
591 | MacroDefKind::BuiltInAttr(id, _)
592 | MacroDefKind::BuiltInDerive(id, _)
593 | MacroDefKind::BuiltInEager(id, _) => Either::Left(id),
594 }
595 }
596
597 pub fn is_proc_macro(&self) -> bool {
598 matches!(self.kind, MacroDefKind::ProcMacro(..))
599 }
600
601 pub fn is_attribute(&self) -> bool {
602 match self.kind {
603 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => {
604 true
605 }
606 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR),
607 _ => false,
608 }
609 }
610
611 pub fn is_derive(&self) -> bool {
612 match self.kind {
613 MacroDefKind::BuiltInDerive(..)
614 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true,
615 MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE),
616 _ => false,
617 }
618 }
619
620 pub fn is_fn_like(&self) -> bool {
621 matches!(
622 self.kind,
623 MacroDefKind::BuiltIn(..)
624 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
625 | MacroDefKind::BuiltInEager(..)
626 | MacroDefKind::Declarative(..)
627 )
628 }
629
630 pub fn is_attribute_derive(&self) -> bool {
631 matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
632 }
633
634 pub fn is_include(&self) -> bool {
635 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
636 }
637
638 pub fn is_include_like(&self) -> bool {
639 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
640 }
641
642 pub fn is_env_or_option_env(&self) -> bool {
643 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
644 }
645}
646
647impl MacroCallLoc {
648 pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
649 match &self.kind {
650 MacroCallKind::FnLike { ast_id, .. } => {
651 ast_id.with_value(ast_id.to_node(db).syntax().clone())
652 }
653 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
654 let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
656 ast_id.with_value(attr.syntax().clone())
657 }
658 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
659 if self.def.is_attribute_derive() {
660 let (attr, _, _, _) =
661 attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
662 ast_id.with_value(attr.syntax().clone())
663 } else {
664 ast_id.with_value(ast_id.to_node(db).syntax().clone())
665 }
666 }
667 }
668 }
669
670 pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile<ast::Item> {
671 match self.kind {
672 MacroCallKind::FnLike { ast_id, .. } => {
673 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
674 }
675 MacroCallKind::Derive { ast_id, .. } => {
676 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
677 }
678 MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
679 }
680 }
681
682 fn expand_to(&self) -> ExpandTo {
683 match self.kind {
684 MacroCallKind::FnLike { expand_to, .. } => expand_to,
685 MacroCallKind::Derive { .. } => ExpandTo::Items,
686 MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
687 MacroCallKind::Attr { .. } => {
688 ExpandTo::Items
690 }
691 }
692 }
693
694 pub fn include_file_id(
695 &self,
696 db: &dyn ExpandDatabase,
697 macro_call_id: MacroCallId,
698 ) -> Option<EditionedFileId> {
699 if self.def.is_include()
700 && let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind
701 && let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg)
702 {
703 return Some(it);
704 }
705
706 None
707 }
708}
709
710impl MacroCallKind {
711 pub fn descr(&self) -> &'static str {
712 match self {
713 MacroCallKind::FnLike { .. } => "macro call",
714 MacroCallKind::Derive { .. } => "derive macro",
715 MacroCallKind::Attr { .. } => "attribute macro",
716 }
717 }
718
719 pub fn file_id(&self) -> HirFileId {
721 match *self {
722 MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
723 | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
724 | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
725 }
726 }
727
728 pub fn erased_ast_id(&self) -> ErasedFileAstId {
729 match *self {
730 MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
731 MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
732 MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
733 }
734 }
735
736 pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange {
743 let mut kind = self;
744 let file_id = loop {
745 match kind.file_id() {
746 HirFileId::MacroFile(file) => {
747 kind = db.lookup_intern_macro_call(file).kind;
748 }
749 HirFileId::FileId(file_id) => break file_id,
750 }
751 };
752
753 let range = match kind {
754 MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
755 MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(),
756 MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(),
757 };
758
759 FileRange { range, file_id }
760 }
761
762 pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange {
768 let mut kind = self;
769 let file_id = loop {
770 match kind.file_id() {
771 HirFileId::MacroFile(file) => {
772 kind = db.lookup_intern_macro_call(file).kind;
773 }
774 HirFileId::FileId(file_id) => break file_id,
775 }
776 };
777
778 let range = match kind {
779 MacroCallKind::FnLike { ast_id, .. } => {
780 let node = ast_id.to_node(db);
781 node.path()
782 .unwrap()
783 .syntax()
784 .text_range()
785 .cover(node.excl_token().unwrap().text_range())
786 }
787 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
788 derive_attr_index.find_attr_range(db, krate, ast_id).2
790 }
791 MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
793 attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2
794 }
795 };
796
797 FileRange { range, file_id }
798 }
799
800 fn arg(&self, db: &dyn ExpandDatabase) -> InFile<Option<SyntaxNode>> {
801 match self {
802 MacroCallKind::FnLike { ast_id, .. } => {
803 ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
804 }
805 MacroCallKind::Derive { ast_id, .. } => {
806 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
807 }
808 MacroCallKind::Attr { ast_id, .. } => {
809 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
810 }
811 }
812 }
813}
814
815#[derive(Clone, Debug, PartialEq, Eq)]
819pub struct ExpansionInfo {
820 expanded: InMacroFile<SyntaxNode>,
821 arg: InFile<Option<SyntaxNode>>,
823 exp_map: Arc<ExpansionSpanMap>,
824 arg_map: SpanMap,
825 loc: MacroCallLoc,
826}
827
828impl ExpansionInfo {
829 pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
830 self.expanded.clone()
831 }
832
833 pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
834 self.arg.as_ref().map(|it| it.as_ref())
835 }
836
837 pub fn call_file(&self) -> HirFileId {
838 self.arg.file_id
839 }
840
841 pub fn is_attr(&self) -> bool {
842 matches!(
843 self.loc.def.kind,
844 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
845 )
846 }
847
848 pub fn map_range_down_exact(
854 &self,
855 span: Span,
856 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
857 let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
858 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
859 });
860
861 Some(InMacroFile::new(self.expanded.file_id, tokens))
862 }
863
864 pub fn map_range_down(
869 &self,
870 span: Span,
871 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
872 let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
873 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
874 });
875
876 Some(InMacroFile::new(self.expanded.file_id, tokens))
877 }
878
879 pub fn span_for_offset(
881 &self,
882 db: &dyn ExpandDatabase,
883 offset: TextSize,
884 ) -> (FileRange, SyntaxContext) {
885 debug_assert!(self.expanded.value.text_range().contains(offset));
886 span_for_offset(db, &self.exp_map, offset)
887 }
888
889 pub fn map_node_range_up(
891 &self,
892 db: &dyn ExpandDatabase,
893 range: TextRange,
894 ) -> Option<(FileRange, SyntaxContext)> {
895 debug_assert!(self.expanded.value.text_range().contains_range(range));
896 map_node_range_up(db, &self.exp_map, range)
897 }
898
899 pub fn map_range_up_once(
904 &self,
905 db: &dyn ExpandDatabase,
906 token: TextRange,
907 ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
908 debug_assert!(self.expanded.value.text_range().contains_range(token));
909 let span = self.exp_map.span_at(token.start());
910 match &self.arg_map {
911 SpanMap::RealSpanMap(_) => {
912 let file_id =
913 EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into();
914 let anchor_offset =
915 db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
916 InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
917 }
918 SpanMap::ExpansionSpanMap(arg_map) => {
919 let Some(arg_node) = &self.arg.value else {
920 return InFile::new(self.arg.file_id, smallvec::smallvec![]);
921 };
922 let arg_range = arg_node.text_range();
923 InFile::new(
924 self.arg.file_id,
925 arg_map
926 .ranges_with_span_exact(span)
927 .filter(|(range, _)| range.intersect(arg_range).is_some())
928 .map(TupleExt::head)
929 .collect(),
930 )
931 }
932 }
933 }
934
935 pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo {
936 let _p = tracing::info_span!("ExpansionInfo::new").entered();
937 let loc = db.lookup_intern_macro_call(macro_file);
938
939 let arg_tt = loc.kind.arg(db);
940 let arg_map = db.span_map(arg_tt.file_id);
941
942 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
943 let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
944
945 ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
946 }
947}
948
949pub fn map_node_range_up_rooted(
953 db: &dyn ExpandDatabase,
954 exp_map: &ExpansionSpanMap,
955 range: TextRange,
956) -> Option<FileRange> {
957 let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
958 let Span { range, anchor, ctx: _ } = spans.next()?;
959 let mut start = range.start();
960 let mut end = range.end();
961
962 for span in spans {
963 if span.anchor != anchor {
964 return None;
965 }
966 start = start.min(span.range.start());
967 end = end.max(span.range.end());
968 }
969 let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
970 let anchor_offset =
971 db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
972 Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset })
973}
974
975pub fn map_node_range_up(
979 db: &dyn ExpandDatabase,
980 exp_map: &ExpansionSpanMap,
981 range: TextRange,
982) -> Option<(FileRange, SyntaxContext)> {
983 let mut spans = exp_map.spans_for_range(range);
984 let Span { range, anchor, ctx } = spans.next()?;
985 let mut start = range.start();
986 let mut end = range.end();
987
988 for span in spans {
989 if span.anchor != anchor || span.ctx != ctx {
990 return None;
991 }
992 start = start.min(span.range.start());
993 end = end.max(span.range.end());
994 }
995 let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
996 let anchor_offset =
997 db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
998 Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx))
999}
1000
1001pub fn span_for_offset(
1003 db: &dyn ExpandDatabase,
1004 exp_map: &ExpansionSpanMap,
1005 offset: TextSize,
1006) -> (FileRange, SyntaxContext) {
1007 let span = exp_map.span_at(offset);
1008 let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id);
1009 let anchor_offset =
1010 db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start();
1011 (FileRange { file_id, range: span.range + anchor_offset }, span.ctx)
1012}
1013
1014#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1030pub enum ExpandTo {
1031 Statements,
1032 Items,
1033 Pattern,
1034 Type,
1035 Expr,
1036}
1037
1038impl ExpandTo {
1039 pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1040 use syntax::SyntaxKind::*;
1041
1042 let syn = call.syntax();
1043
1044 let parent = match syn.parent() {
1045 Some(it) => it,
1046 None => return ExpandTo::Statements,
1047 };
1048
1049 if parent.kind() == MACRO_EXPR
1052 && parent
1053 .parent()
1054 .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1055 {
1056 return ExpandTo::Statements;
1057 }
1058
1059 match parent.kind() {
1060 MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1061 MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1062 MACRO_PAT => ExpandTo::Pattern,
1063 MACRO_TYPE => ExpandTo::Type,
1064
1065 ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1066 | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1067 | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1068 | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1069 | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1070 _ => {
1071 ExpandTo::Items
1073 }
1074 }
1075 }
1076}
1077
1078intern::impl_internable!(ModPath);
1079
1080#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
1081#[doc(alias = "MacroFileId")]
1082pub struct MacroCallId {
1083 pub loc: MacroCallLoc,
1084}
1085
1086impl From<span::MacroCallId> for MacroCallId {
1087 #[inline]
1088 fn from(value: span::MacroCallId) -> Self {
1089 MacroCallId::from_id(value.0)
1090 }
1091}
1092
1093impl From<MacroCallId> for span::MacroCallId {
1094 #[inline]
1095 fn from(value: MacroCallId) -> span::MacroCallId {
1096 span::MacroCallId(value.as_id())
1097 }
1098}
1099
1100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
1101pub enum HirFileId {
1102 FileId(EditionedFileId),
1103 MacroFile(MacroCallId),
1104}
1105
1106impl From<EditionedFileId> for HirFileId {
1107 #[inline]
1108 fn from(file_id: EditionedFileId) -> Self {
1109 HirFileId::FileId(file_id)
1110 }
1111}
1112
1113impl From<MacroCallId> for HirFileId {
1114 #[inline]
1115 fn from(file_id: MacroCallId) -> Self {
1116 HirFileId::MacroFile(file_id)
1117 }
1118}
1119
1120impl HirFileId {
1121 #[inline]
1122 pub fn macro_file(self) -> Option<MacroCallId> {
1123 match self {
1124 HirFileId::FileId(_) => None,
1125 HirFileId::MacroFile(it) => Some(it),
1126 }
1127 }
1128
1129 #[inline]
1130 pub fn is_macro(self) -> bool {
1131 matches!(self, HirFileId::MacroFile(_))
1132 }
1133
1134 #[inline]
1135 pub fn file_id(self) -> Option<EditionedFileId> {
1136 match self {
1137 HirFileId::FileId(it) => Some(it),
1138 HirFileId::MacroFile(_) => None,
1139 }
1140 }
1141
1142 #[inline]
1143 pub fn krate(self, db: &dyn ExpandDatabase) -> Crate {
1144 match self {
1145 HirFileId::FileId(it) => it.krate(db),
1146 HirFileId::MacroFile(it) => it.loc(db).krate,
1147 }
1148 }
1149}
1150
1151impl PartialEq<EditionedFileId> for HirFileId {
1152 fn eq(&self, &other: &EditionedFileId) -> bool {
1153 *self == HirFileId::from(other)
1154 }
1155}
1156impl PartialEq<HirFileId> for EditionedFileId {
1157 fn eq(&self, &other: &HirFileId) -> bool {
1158 other == HirFileId::from(*self)
1159 }
1160}