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                        && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind
370                        && let Ok(it) = include_input_to_file_id(db, file, &eager.arg)
371                    {
372                        break it;
373                    }
374                    self = loc.kind.file_id();
375                }
376            }
377        }
378    }
379
380    pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>> {
381        let mut call = db.lookup_intern_macro_call(self.macro_file()?).to_node(db);
382        loop {
383            match call.file_id {
384                HirFileId::FileId(file_id) => {
385                    break Some(InRealFile { file_id, value: call.value });
386                }
387                HirFileId::MacroFile(macro_call_id) => {
388                    call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
389                }
390            }
391        }
392    }
393
394    pub fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>> {
395        Some(db.lookup_intern_macro_call(self.macro_file()?).to_node(db))
396    }
397
398    pub fn as_builtin_derive_attr_node(
399        &self,
400        db: &dyn ExpandDatabase,
401    ) -> Option<InFile<ast::Attr>> {
402        let macro_file = self.macro_file()?;
403        let loc = db.lookup_intern_macro_call(macro_file);
404        let attr = match loc.def.kind {
405            MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
406            _ => return None,
407        };
408        Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
409    }
410}
411
412#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
413pub enum MacroKind {
414    /// `macro_rules!` or Macros 2.0 macro.
415    Declarative,
416    /// A built-in function-like macro.
417    DeclarativeBuiltIn,
418    /// A custom derive.
419    Derive,
420    /// A builtin-in derive.
421    DeriveBuiltIn,
422    /// A procedural attribute macro.
423    Attr,
424    /// A built-in attribute macro.
425    AttrBuiltIn,
426    /// A function-like procedural macro.
427    ProcMacro,
428}
429
430impl MacroCallId {
431    pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
432        db.lookup_intern_macro_call(self).to_node(db)
433    }
434    pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 {
435        let mut level = 0;
436        let mut macro_file = self;
437        loop {
438            let loc = db.lookup_intern_macro_call(macro_file);
439
440            level += 1;
441            macro_file = match loc.kind.file_id() {
442                HirFileId::FileId(_) => break level,
443                HirFileId::MacroFile(it) => it,
444            };
445        }
446    }
447    pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId {
448        db.lookup_intern_macro_call(self).kind.file_id()
449    }
450
451    /// Return expansion information if it is a macro-expansion file
452    pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo {
453        ExpansionInfo::new(db, self)
454    }
455
456    pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind {
457        match db.lookup_intern_macro_call(self).def.kind {
458            MacroDefKind::Declarative(..) => MacroKind::Declarative,
459            MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
460                MacroKind::DeclarativeBuiltIn
461            }
462            MacroDefKind::BuiltInDerive(..) => MacroKind::DeriveBuiltIn,
463            MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => MacroKind::Derive,
464            MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr,
465            MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro,
466            MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn,
467        }
468    }
469
470    pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool {
471        db.lookup_intern_macro_call(self).def.is_include()
472    }
473
474    pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool {
475        db.lookup_intern_macro_call(self).def.is_include_like()
476    }
477
478    pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool {
479        db.lookup_intern_macro_call(self).def.is_env_or_option_env()
480    }
481
482    pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool {
483        let loc = db.lookup_intern_macro_call(self);
484        matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
485    }
486
487    pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
488        let loc = db.lookup_intern_macro_call(self);
489        match &loc.kind {
490            MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
491            _ => None,
492        }
493    }
494
495    pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool {
496        let loc = db.lookup_intern_macro_call(self);
497        loc.def.is_attribute_derive()
498    }
499}
500
501impl MacroDefId {
502    pub fn make_call(
503        self,
504        db: &dyn ExpandDatabase,
505        krate: Crate,
506        kind: MacroCallKind,
507        ctxt: SyntaxContext,
508    ) -> MacroCallId {
509        db.intern_macro_call(MacroCallLoc { def: self, krate, kind, ctxt })
510    }
511
512    pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
513        match self.kind {
514            MacroDefKind::Declarative(id)
515            | MacroDefKind::BuiltIn(id, _)
516            | MacroDefKind::BuiltInAttr(id, _)
517            | MacroDefKind::BuiltInDerive(id, _)
518            | MacroDefKind::BuiltInEager(id, _) => {
519                id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
520            }
521            MacroDefKind::ProcMacro(id, _, _) => {
522                id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
523            }
524        }
525    }
526
527    pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
528        match self.kind {
529            MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
530            MacroDefKind::Declarative(id)
531            | MacroDefKind::BuiltIn(id, _)
532            | MacroDefKind::BuiltInAttr(id, _)
533            | MacroDefKind::BuiltInDerive(id, _)
534            | MacroDefKind::BuiltInEager(id, _) => Either::Left(id),
535        }
536    }
537
538    pub fn is_proc_macro(&self) -> bool {
539        matches!(self.kind, MacroDefKind::ProcMacro(..))
540    }
541
542    pub fn is_attribute(&self) -> bool {
543        matches!(
544            self.kind,
545            MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
546        )
547    }
548
549    pub fn is_derive(&self) -> bool {
550        matches!(
551            self.kind,
552            MacroDefKind::BuiltInDerive(..)
553                | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
554        )
555    }
556
557    pub fn is_fn_like(&self) -> bool {
558        matches!(
559            self.kind,
560            MacroDefKind::BuiltIn(..)
561                | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
562                | MacroDefKind::BuiltInEager(..)
563                | MacroDefKind::Declarative(..)
564        )
565    }
566
567    pub fn is_attribute_derive(&self) -> bool {
568        matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
569    }
570
571    pub fn is_include(&self) -> bool {
572        matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
573    }
574
575    pub fn is_include_like(&self) -> bool {
576        matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
577    }
578
579    pub fn is_env_or_option_env(&self) -> bool {
580        matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
581    }
582}
583
584impl MacroCallLoc {
585    pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
586        match self.kind {
587            MacroCallKind::FnLike { ast_id, .. } => {
588                ast_id.with_value(ast_id.to_node(db).syntax().clone())
589            }
590            MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
591                // FIXME: handle `cfg_attr`
592                ast_id.with_value(ast_id.to_node(db)).map(|it| {
593                    collect_attrs(&it)
594                        .nth(derive_attr_index.ast_index())
595                        .and_then(|it| match it.1 {
596                            Either::Left(attr) => Some(attr.syntax().clone()),
597                            Either::Right(_) => None,
598                        })
599                        .unwrap_or_else(|| it.syntax().clone())
600                })
601            }
602            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
603                if self.def.is_attribute_derive() {
604                    // FIXME: handle `cfg_attr`
605                    ast_id.with_value(ast_id.to_node(db)).map(|it| {
606                        collect_attrs(&it)
607                            .nth(invoc_attr_index.ast_index())
608                            .and_then(|it| match it.1 {
609                                Either::Left(attr) => Some(attr.syntax().clone()),
610                                Either::Right(_) => None,
611                            })
612                            .unwrap_or_else(|| it.syntax().clone())
613                    })
614                } else {
615                    ast_id.with_value(ast_id.to_node(db).syntax().clone())
616                }
617            }
618        }
619    }
620
621    pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile<ast::Item> {
622        match self.kind {
623            MacroCallKind::FnLike { ast_id, .. } => {
624                InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
625            }
626            MacroCallKind::Derive { ast_id, .. } => {
627                InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
628            }
629            MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
630        }
631    }
632
633    fn expand_to(&self) -> ExpandTo {
634        match self.kind {
635            MacroCallKind::FnLike { expand_to, .. } => expand_to,
636            MacroCallKind::Derive { .. } => ExpandTo::Items,
637            MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
638            MacroCallKind::Attr { .. } => {
639                // FIXME(stmt_expr_attributes)
640                ExpandTo::Items
641            }
642        }
643    }
644
645    pub fn include_file_id(
646        &self,
647        db: &dyn ExpandDatabase,
648        macro_call_id: MacroCallId,
649    ) -> Option<EditionedFileId> {
650        if self.def.is_include()
651            && let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind
652            && let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg)
653        {
654            return Some(it);
655        }
656
657        None
658    }
659}
660
661impl MacroCallKind {
662    pub fn descr(&self) -> &'static str {
663        match self {
664            MacroCallKind::FnLike { .. } => "macro call",
665            MacroCallKind::Derive { .. } => "derive macro",
666            MacroCallKind::Attr { .. } => "attribute macro",
667        }
668    }
669
670    /// Returns the file containing the macro invocation.
671    pub fn file_id(&self) -> HirFileId {
672        match *self {
673            MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
674            | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
675            | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
676        }
677    }
678
679    pub fn erased_ast_id(&self) -> ErasedFileAstId {
680        match *self {
681            MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
682            MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
683            MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
684        }
685    }
686
687    /// Returns the original file range that best describes the location of this macro call.
688    ///
689    /// This spans the entire macro call, including its input. That is for
690    /// - fn_like! {}, it spans the path and token tree
691    /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item
692    /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item
693    pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange {
694        let mut kind = self;
695        let file_id = loop {
696            match kind.file_id() {
697                HirFileId::MacroFile(file) => {
698                    kind = db.lookup_intern_macro_call(file).kind;
699                }
700                HirFileId::FileId(file_id) => break file_id,
701            }
702        };
703
704        let range = match kind {
705            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
706            MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(),
707            MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(),
708        };
709
710        FileRange { range, file_id }
711    }
712
713    /// Returns the original file range that best describes the location of this macro call.
714    ///
715    /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
716    /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the
717    /// attribute's range, and derives get only the specific derive that is being referred to.
718    pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange {
719        let mut kind = self;
720        let file_id = loop {
721            match kind.file_id() {
722                HirFileId::MacroFile(file) => {
723                    kind = db.lookup_intern_macro_call(file).kind;
724                }
725                HirFileId::FileId(file_id) => break file_id,
726            }
727        };
728
729        let range = match kind {
730            MacroCallKind::FnLike { ast_id, .. } => {
731                let node = ast_id.to_node(db);
732                node.path()
733                    .unwrap()
734                    .syntax()
735                    .text_range()
736                    .cover(node.excl_token().unwrap().text_range())
737            }
738            MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
739                // FIXME: should be the range of the macro name, not the whole derive
740                // FIXME: handle `cfg_attr`
741                collect_attrs(&ast_id.to_node(db))
742                    .nth(derive_attr_index.ast_index())
743                    .expect("missing derive")
744                    .1
745                    .expect_left("derive is a doc comment?")
746                    .syntax()
747                    .text_range()
748            }
749            // FIXME: handle `cfg_attr`
750            MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
751                collect_attrs(&ast_id.to_node(db))
752                    .nth(invoc_attr_index.ast_index())
753                    .expect("missing attribute")
754                    .1
755                    .expect_left("attribute macro is a doc comment?")
756                    .syntax()
757                    .text_range()
758            }
759        };
760
761        FileRange { range, file_id }
762    }
763
764    fn arg(&self, db: &dyn ExpandDatabase) -> InFile<Option<SyntaxNode>> {
765        match self {
766            MacroCallKind::FnLike { ast_id, .. } => {
767                ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
768            }
769            MacroCallKind::Derive { ast_id, .. } => {
770                ast_id.to_in_file_node(db).syntax().cloned().map(Some)
771            }
772            MacroCallKind::Attr { ast_id, .. } => {
773                ast_id.to_in_file_node(db).syntax().cloned().map(Some)
774            }
775        }
776    }
777}
778
779/// ExpansionInfo mainly describes how to map text range between src and expanded macro
780// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
781// simpler function calls if the map is only used once
782#[derive(Clone, Debug, PartialEq, Eq)]
783pub struct ExpansionInfo {
784    expanded: InMacroFile<SyntaxNode>,
785    /// The argument TokenTree or item for attributes
786    arg: InFile<Option<SyntaxNode>>,
787    exp_map: Arc<ExpansionSpanMap>,
788    arg_map: SpanMap,
789    loc: MacroCallLoc,
790}
791
792impl ExpansionInfo {
793    pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
794        self.expanded.clone()
795    }
796
797    pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
798        self.arg.as_ref().map(|it| it.as_ref())
799    }
800
801    pub fn call_file(&self) -> HirFileId {
802        self.arg.file_id
803    }
804
805    pub fn is_attr(&self) -> bool {
806        matches!(
807            self.loc.def.kind,
808            MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
809        )
810    }
811
812    /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
813    ///
814    /// Note this does a linear search through the entire backing vector of the spanmap.
815    // FIXME: Consider adding a reverse map to ExpansionInfo to get rid of the linear search which
816    // potentially results in quadratic look ups (notably this might improve semantic highlighting perf)
817    pub fn map_range_down_exact(
818        &self,
819        span: Span,
820    ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
821        let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
822            self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
823        });
824
825        Some(InMacroFile::new(self.expanded.file_id, tokens))
826    }
827
828    /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
829    /// Unlike [`map_range_down_exact`], this will consider spans that contain the given span.
830    ///
831    /// Note this does a linear search through the entire backing vector of the spanmap.
832    pub fn map_range_down(
833        &self,
834        span: Span,
835    ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
836        let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
837            self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
838        });
839
840        Some(InMacroFile::new(self.expanded.file_id, tokens))
841    }
842
843    /// Looks up the span at the given offset.
844    pub fn span_for_offset(
845        &self,
846        db: &dyn ExpandDatabase,
847        offset: TextSize,
848    ) -> (FileRange, SyntaxContext) {
849        debug_assert!(self.expanded.value.text_range().contains(offset));
850        span_for_offset(db, &self.exp_map, offset)
851    }
852
853    /// Maps up the text range out of the expansion hierarchy back into the original file its from.
854    pub fn map_node_range_up(
855        &self,
856        db: &dyn ExpandDatabase,
857        range: TextRange,
858    ) -> Option<(FileRange, SyntaxContext)> {
859        debug_assert!(self.expanded.value.text_range().contains_range(range));
860        map_node_range_up(db, &self.exp_map, range)
861    }
862
863    /// Maps up the text range out of the expansion into its macro call.
864    ///
865    /// Note that this may return multiple ranges as we lose the precise association between input to output
866    /// and as such we may consider inputs that are unrelated.
867    pub fn map_range_up_once(
868        &self,
869        db: &dyn ExpandDatabase,
870        token: TextRange,
871    ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
872        debug_assert!(self.expanded.value.text_range().contains_range(token));
873        let span = self.exp_map.span_at(token.start());
874        match &self.arg_map {
875            SpanMap::RealSpanMap(_) => {
876                let file_id = EditionedFileId::from_span(db, span.anchor.file_id).into();
877                let anchor_offset =
878                    db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
879                InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
880            }
881            SpanMap::ExpansionSpanMap(arg_map) => {
882                let Some(arg_node) = &self.arg.value else {
883                    return InFile::new(self.arg.file_id, smallvec::smallvec![]);
884                };
885                let arg_range = arg_node.text_range();
886                InFile::new(
887                    self.arg.file_id,
888                    arg_map
889                        .ranges_with_span_exact(span)
890                        .filter(|(range, _)| range.intersect(arg_range).is_some())
891                        .map(TupleExt::head)
892                        .collect(),
893                )
894            }
895        }
896    }
897
898    pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo {
899        let _p = tracing::info_span!("ExpansionInfo::new").entered();
900        let loc = db.lookup_intern_macro_call(macro_file);
901
902        let arg_tt = loc.kind.arg(db);
903        let arg_map = db.span_map(arg_tt.file_id);
904
905        let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
906        let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
907
908        ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
909    }
910}
911
912/// Maps up the text range out of the expansion hierarchy back into the original file its from only
913/// considering the root spans contained.
914/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ.
915pub fn map_node_range_up_rooted(
916    db: &dyn ExpandDatabase,
917    exp_map: &ExpansionSpanMap,
918    range: TextRange,
919) -> Option<FileRange> {
920    let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
921    let Span { range, anchor, ctx: _ } = spans.next()?;
922    let mut start = range.start();
923    let mut end = range.end();
924
925    for span in spans {
926        if span.anchor != anchor {
927            return None;
928        }
929        start = start.min(span.range.start());
930        end = end.max(span.range.end());
931    }
932    let file_id = EditionedFileId::from_span(db, anchor.file_id);
933    let anchor_offset =
934        db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
935    Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset })
936}
937
938/// Maps up the text range out of the expansion hierarchy back into the original file its from.
939///
940/// this will return `None` if any anchors or syntax contexts differ.
941pub fn map_node_range_up(
942    db: &dyn ExpandDatabase,
943    exp_map: &ExpansionSpanMap,
944    range: TextRange,
945) -> Option<(FileRange, SyntaxContext)> {
946    let mut spans = exp_map.spans_for_range(range);
947    let Span { range, anchor, ctx } = spans.next()?;
948    let mut start = range.start();
949    let mut end = range.end();
950
951    for span in spans {
952        if span.anchor != anchor || span.ctx != ctx {
953            return None;
954        }
955        start = start.min(span.range.start());
956        end = end.max(span.range.end());
957    }
958    let file_id = EditionedFileId::from_span(db, anchor.file_id);
959    let anchor_offset =
960        db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
961    Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx))
962}
963
964/// Maps up the text range out of the expansion hierarchy back into the original file its from.
965/// This version will aggregate the ranges of all spans with the same anchor and syntax context.
966pub fn map_node_range_up_aggregated(
967    db: &dyn ExpandDatabase,
968    exp_map: &ExpansionSpanMap,
969    range: TextRange,
970) -> FxHashMap<(SpanAnchor, SyntaxContext), TextRange> {
971    let mut map = FxHashMap::default();
972    for span in exp_map.spans_for_range(range) {
973        let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range);
974        *range = TextRange::new(
975            range.start().min(span.range.start()),
976            range.end().max(span.range.end()),
977        );
978    }
979    for ((anchor, _), range) in &mut map {
980        let file_id = EditionedFileId::from_span(db, anchor.file_id);
981        let anchor_offset =
982            db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
983        *range += anchor_offset;
984    }
985    map
986}
987
988/// Looks up the span at the given offset.
989pub fn span_for_offset(
990    db: &dyn ExpandDatabase,
991    exp_map: &ExpansionSpanMap,
992    offset: TextSize,
993) -> (FileRange, SyntaxContext) {
994    let span = exp_map.span_at(offset);
995    let file_id = EditionedFileId::from_span(db, span.anchor.file_id);
996    let anchor_offset =
997        db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start();
998    (FileRange { file_id, range: span.range + anchor_offset }, span.ctx)
999}
1000
1001/// In Rust, macros expand token trees to token trees. When we want to turn a
1002/// token tree into an AST node, we need to figure out what kind of AST node we
1003/// want: something like `foo` can be a type, an expression, or a pattern.
1004///
1005/// Naively, one would think that "what this expands to" is a property of a
1006/// particular macro: macro `m1` returns an item, while macro `m2` returns an
1007/// expression, etc. That's not the case -- macros are polymorphic in the
1008/// result, and can expand to any type of the AST node.
1009///
1010/// What defines the actual AST node is the syntactic context of the macro
1011/// invocation. As a contrived example, in `let T![*] = T![*];` the first `T`
1012/// expands to a pattern, while the second one expands to an expression.
1013///
1014/// `ExpandTo` captures this bit of information about a particular macro call
1015/// site.
1016#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1017pub enum ExpandTo {
1018    Statements,
1019    Items,
1020    Pattern,
1021    Type,
1022    Expr,
1023}
1024
1025impl ExpandTo {
1026    pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1027        use syntax::SyntaxKind::*;
1028
1029        let syn = call.syntax();
1030
1031        let parent = match syn.parent() {
1032            Some(it) => it,
1033            None => return ExpandTo::Statements,
1034        };
1035
1036        // FIXME: macros in statement position are treated as expression statements, they should
1037        // probably be their own statement kind. The *grand*parent indicates what's valid.
1038        if parent.kind() == MACRO_EXPR
1039            && parent
1040                .parent()
1041                .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1042        {
1043            return ExpandTo::Statements;
1044        }
1045
1046        match parent.kind() {
1047            MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1048            MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1049            MACRO_PAT => ExpandTo::Pattern,
1050            MACRO_TYPE => ExpandTo::Type,
1051
1052            ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1053            | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1054            | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1055            | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1056            | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1057            _ => {
1058                // Unknown , Just guess it is `Items`
1059                ExpandTo::Items
1060            }
1061        }
1062    }
1063}
1064
1065intern::impl_internable!(ModPath, attrs::AttrInput);
1066
1067#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
1068#[doc(alias = "MacroFileId")]
1069pub struct MacroCallId {
1070    pub loc: MacroCallLoc,
1071}
1072
1073impl From<span::MacroCallId> for MacroCallId {
1074    #[inline]
1075    fn from(value: span::MacroCallId) -> Self {
1076        MacroCallId::from_id(value.0)
1077    }
1078}
1079
1080impl From<MacroCallId> for span::MacroCallId {
1081    #[inline]
1082    fn from(value: MacroCallId) -> span::MacroCallId {
1083        span::MacroCallId(value.as_id())
1084    }
1085}
1086
1087#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
1088pub enum HirFileId {
1089    FileId(EditionedFileId),
1090    MacroFile(MacroCallId),
1091}
1092
1093impl From<EditionedFileId> for HirFileId {
1094    #[inline]
1095    fn from(file_id: EditionedFileId) -> Self {
1096        HirFileId::FileId(file_id)
1097    }
1098}
1099
1100impl From<MacroCallId> for HirFileId {
1101    #[inline]
1102    fn from(file_id: MacroCallId) -> Self {
1103        HirFileId::MacroFile(file_id)
1104    }
1105}
1106
1107impl HirFileId {
1108    #[inline]
1109    pub fn macro_file(self) -> Option<MacroCallId> {
1110        match self {
1111            HirFileId::FileId(_) => None,
1112            HirFileId::MacroFile(it) => Some(it),
1113        }
1114    }
1115
1116    #[inline]
1117    pub fn is_macro(self) -> bool {
1118        matches!(self, HirFileId::MacroFile(_))
1119    }
1120
1121    #[inline]
1122    pub fn file_id(self) -> Option<EditionedFileId> {
1123        match self {
1124            HirFileId::FileId(it) => Some(it),
1125            HirFileId::MacroFile(_) => None,
1126        }
1127    }
1128}
1129
1130impl PartialEq<EditionedFileId> for HirFileId {
1131    fn eq(&self, &other: &EditionedFileId) -> bool {
1132        *self == HirFileId::from(other)
1133    }
1134}
1135impl PartialEq<HirFileId> for EditionedFileId {
1136    fn eq(&self, &other: &HirFileId) -> bool {
1137        other == HirFileId::from(*self)
1138    }
1139}