1#![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
107pub 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 ProcMacroAttrExpansionDisabled,
158 MissingProcMacroExpander(Crate),
159 MacroDisabled,
161 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 arg: Arc<tt::TopSubtree>,
299 arg_id: MacroCallId,
301 error: Option<ExpandError>,
302 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 eager: Option<Arc<EagerCallInfo>>,
316 },
317 Derive {
318 ast_id: AstId<ast::Adt>,
319 derive_attr_index: AttrId,
324 derive_index: u32,
326 derive_macro_id: MacroCallId,
329 },
330 Attr {
331 ast_id: AstId<ast::Item>,
332 attr_args: Option<Arc<tt::TopSubtree>>,
335 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 Declarative,
416 DeclarativeBuiltIn,
418 Derive,
420 DeriveBuiltIn,
422 Attr,
424 AttrBuiltIn,
426 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 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 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 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 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 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 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 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 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 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#[derive(Clone, Debug, PartialEq, Eq)]
783pub struct ExpansionInfo {
784 expanded: InMacroFile<SyntaxNode>,
785 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 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 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 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 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 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
912pub 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
938pub 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
964pub 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
988pub 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#[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 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 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}