Skip to main content

hir_expand/
lib.rs

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