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