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