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