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