hir_expand/
lib.rs

1//! `hir_expand` deals with macro expansion.
2//!
3//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
4//! tree originates not from the text of some `FileId`, but from some macro
5//! expansion.
6#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
7// It's useful to refer to code that is private in doc comments.
8#![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
110// ideally these would be defined in base-db, but the orphan rule doesn't let us
111pub 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    /// Attribute macro expansion is disabled.
160    ProcMacroAttrExpansionDisabled,
161    MissingProcMacroExpander(Crate),
162    /// The macro for this call is disabled.
163    MacroDisabled,
164    /// The macro definition has errors.
165    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    /// The expanded argument of the eager macro.
301    arg: Arc<tt::TopSubtree>,
302    /// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
303    arg_id: MacroCallId,
304    error: Option<ExpandError>,
305    /// The call site span of the eager macro
306    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        /// Some if this is a macro call for an eager macro. Note that this is `None`
315        /// for the eager input macro file.
316        // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing
317        // leakage problems here
318        eager: Option<Arc<EagerCallInfo>>,
319    },
320    Derive {
321        ast_id: AstId<ast::Adt>,
322        /// Syntactical index of the invoking `#[derive]` attribute.
323        derive_attr_index: AttrId,
324        /// Index of the derive macro in the derive attribute
325        derive_index: u32,
326        /// The "parent" macro call.
327        /// We will resolve the same token tree for all derive macros in the same derive attribute.
328        derive_macro_id: MacroCallId,
329    },
330    Attr {
331        ast_id: AstId<ast::Item>,
332        // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`.
333        attr_args: Option<Arc<tt::TopSubtree>>,
334        /// This contains the list of all *active* attributes (derives and attr macros) preceding this
335        /// attribute, including this attribute. You can retrieve the [`AttrId`] of the current attribute
336        /// by calling [`invoc_attr()`] on this.
337        ///
338        /// The macro should not see the attributes here.
339        ///
340        /// [`invoc_attr()`]: AttrMacroAttrIds::invoc_attr
341        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    /// `macro_rules!` or Macros 2.0 macro.
476    Declarative,
477    /// A built-in function-like macro.
478    DeclarativeBuiltIn,
479    /// A custom derive.
480    Derive,
481    /// A builtin-in derive.
482    DeriveBuiltIn,
483    /// A procedural attribute macro.
484    Attr,
485    /// A built-in attribute macro.
486    AttrBuiltIn,
487    /// A function-like procedural macro.
488    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    /// Return expansion information if it is a macro-expansion file
513    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                // FIXME: handle `cfg_attr`
657                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                // FIXME(stmt_expr_attributes)
691                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    /// Returns the file containing the macro invocation.
722    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    /// Returns the original file range that best describes the location of this macro call.
739    ///
740    /// This spans the entire macro call, including its input. That is for
741    /// - fn_like! {}, it spans the path and token tree
742    /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item
743    /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item
744    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    /// Returns the original file range that best describes the location of this macro call.
765    ///
766    /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
767    /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the
768    /// attribute's range, and derives get only the specific derive that is being referred to.
769    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                // FIXME: should be the range of the macro name, not the whole derive
791                derive_attr_index.find_attr_range(db, krate, ast_id).2
792            }
793            // FIXME: handle `cfg_attr`
794            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/// ExpansionInfo mainly describes how to map text range between src and expanded macro
818// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
819// simpler function calls if the map is only used once
820#[derive(Clone, Debug, PartialEq, Eq)]
821pub struct ExpansionInfo {
822    expanded: InMacroFile<SyntaxNode>,
823    /// The argument TokenTree or item for attributes
824    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    /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
851    ///
852    /// Note this does a linear search through the entire backing vector of the spanmap.
853    // FIXME: Consider adding a reverse map to ExpansionInfo to get rid of the linear search which
854    // potentially results in quadratic look ups (notably this might improve semantic highlighting perf)
855    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    /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
871    /// Unlike [`ExpansionInfo::map_range_down_exact`], this will consider spans that contain the given span.
872    ///
873    /// Note this does a linear search through the entire backing vector of the spanmap.
874    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    /// Looks up the span at the given offset.
890    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    /// Maps up the text range out of the expansion hierarchy back into the original file its from.
900    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    /// Maps up the text range out of the expansion into its macro call.
910    ///
911    /// Note that this may return multiple ranges as we lose the precise association between input to output
912    /// and as such we may consider inputs that are unrelated.
913    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
959/// Maps up the text range out of the expansion hierarchy back into the original file its from only
960/// considering the root spans contained.
961/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ.
962pub 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
985/// Maps up the text range out of the expansion hierarchy back into the original file its from.
986///
987/// this will return `None` if any anchors or syntax contexts differ.
988pub 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
1011/// Looks up the span at the given offset.
1012pub 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/// In Rust, macros expand token trees to token trees. When we want to turn a
1025/// token tree into an AST node, we need to figure out what kind of AST node we
1026/// want: something like `foo` can be a type, an expression, or a pattern.
1027///
1028/// Naively, one would think that "what this expands to" is a property of a
1029/// particular macro: macro `m1` returns an item, while macro `m2` returns an
1030/// expression, etc. That's not the case -- macros are polymorphic in the
1031/// result, and can expand to any type of the AST node.
1032///
1033/// What defines the actual AST node is the syntactic context of the macro
1034/// invocation. As a contrived example, in `let T![*] = T![*];` the first `T`
1035/// expands to a pattern, while the second one expands to an expression.
1036///
1037/// `ExpandTo` captures this bit of information about a particular macro call
1038/// site.
1039#[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        // FIXME: macros in statement position are treated as expression statements, they should
1060        // probably be their own statement kind. The *grand*parent indicates what's valid.
1061        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                // Unknown , Just guess it is `Items`
1082                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}