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, hard_err)) => RenderedExpandError {
203 message: e.to_owned(),
204 error: hard_err,
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 if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind {
370 if let Ok(it) = include_input_to_file_id(db, file, &eager.arg) {
371 break it;
372 }
373 }
374 }
375 self = loc.kind.file_id();
376 }
377 }
378 }
379 }
380
381 pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>> {
382 let mut call = db.lookup_intern_macro_call(self.macro_file()?).to_node(db);
383 loop {
384 match call.file_id {
385 HirFileId::FileId(file_id) => {
386 break Some(InRealFile { file_id, value: call.value });
387 }
388 HirFileId::MacroFile(macro_call_id) => {
389 call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
390 }
391 }
392 }
393 }
394
395 pub fn as_builtin_derive_attr_node(
396 &self,
397 db: &dyn ExpandDatabase,
398 ) -> Option<InFile<ast::Attr>> {
399 let macro_file = self.macro_file()?;
400 let loc = db.lookup_intern_macro_call(macro_file);
401 let attr = match loc.def.kind {
402 MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
403 _ => return None,
404 };
405 Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
406 }
407}
408
409#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
410pub enum MacroKind {
411 Declarative,
413 DeclarativeBuiltIn,
415 Derive,
417 DeriveBuiltIn,
419 Attr,
421 AttrBuiltIn,
423 ProcMacro,
425}
426
427impl MacroCallId {
428 pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
429 db.lookup_intern_macro_call(self).to_node(db)
430 }
431 pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 {
432 let mut level = 0;
433 let mut macro_file = self;
434 loop {
435 let loc = db.lookup_intern_macro_call(macro_file);
436
437 level += 1;
438 macro_file = match loc.kind.file_id() {
439 HirFileId::FileId(_) => break level,
440 HirFileId::MacroFile(it) => it,
441 };
442 }
443 }
444 pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId {
445 db.lookup_intern_macro_call(self).kind.file_id()
446 }
447
448 pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo {
450 ExpansionInfo::new(db, self)
451 }
452
453 pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind {
454 match db.lookup_intern_macro_call(self).def.kind {
455 MacroDefKind::Declarative(..) => MacroKind::Declarative,
456 MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
457 MacroKind::DeclarativeBuiltIn
458 }
459 MacroDefKind::BuiltInDerive(..) => MacroKind::DeriveBuiltIn,
460 MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => MacroKind::Derive,
461 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr,
462 MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro,
463 MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn,
464 }
465 }
466
467 pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool {
468 db.lookup_intern_macro_call(self).def.is_include()
469 }
470
471 pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool {
472 db.lookup_intern_macro_call(self).def.is_include_like()
473 }
474
475 pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool {
476 db.lookup_intern_macro_call(self).def.is_env_or_option_env()
477 }
478
479 pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool {
480 let loc = db.lookup_intern_macro_call(self);
481 matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
482 }
483
484 pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
485 let loc = db.lookup_intern_macro_call(self);
486 match &loc.kind {
487 MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
488 _ => None,
489 }
490 }
491
492 pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool {
493 let loc = db.lookup_intern_macro_call(self);
494 loc.def.is_attribute_derive()
495 }
496}
497
498impl MacroDefId {
499 pub fn make_call(
500 self,
501 db: &dyn ExpandDatabase,
502 krate: Crate,
503 kind: MacroCallKind,
504 ctxt: SyntaxContext,
505 ) -> MacroCallId {
506 db.intern_macro_call(MacroCallLoc { def: self, krate, kind, ctxt })
507 }
508
509 pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
510 match self.kind {
511 MacroDefKind::Declarative(id)
512 | MacroDefKind::BuiltIn(id, _)
513 | MacroDefKind::BuiltInAttr(id, _)
514 | MacroDefKind::BuiltInDerive(id, _)
515 | MacroDefKind::BuiltInEager(id, _) => {
516 id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
517 }
518 MacroDefKind::ProcMacro(id, _, _) => {
519 id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
520 }
521 }
522 }
523
524 pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
525 match self.kind {
526 MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
527 MacroDefKind::Declarative(id)
528 | MacroDefKind::BuiltIn(id, _)
529 | MacroDefKind::BuiltInAttr(id, _)
530 | MacroDefKind::BuiltInDerive(id, _)
531 | MacroDefKind::BuiltInEager(id, _) => Either::Left(id),
532 }
533 }
534
535 pub fn is_proc_macro(&self) -> bool {
536 matches!(self.kind, MacroDefKind::ProcMacro(..))
537 }
538
539 pub fn is_attribute(&self) -> bool {
540 matches!(
541 self.kind,
542 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
543 )
544 }
545
546 pub fn is_derive(&self) -> bool {
547 matches!(
548 self.kind,
549 MacroDefKind::BuiltInDerive(..)
550 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
551 )
552 }
553
554 pub fn is_fn_like(&self) -> bool {
555 matches!(
556 self.kind,
557 MacroDefKind::BuiltIn(..)
558 | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
559 | MacroDefKind::BuiltInEager(..)
560 | MacroDefKind::Declarative(..)
561 )
562 }
563
564 pub fn is_attribute_derive(&self) -> bool {
565 matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
566 }
567
568 pub fn is_include(&self) -> bool {
569 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
570 }
571
572 pub fn is_include_like(&self) -> bool {
573 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
574 }
575
576 pub fn is_env_or_option_env(&self) -> bool {
577 matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
578 }
579}
580
581impl MacroCallLoc {
582 pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
583 match self.kind {
584 MacroCallKind::FnLike { ast_id, .. } => {
585 ast_id.with_value(ast_id.to_node(db).syntax().clone())
586 }
587 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
588 ast_id.with_value(ast_id.to_node(db)).map(|it| {
590 collect_attrs(&it)
591 .nth(derive_attr_index.ast_index())
592 .and_then(|it| match it.1 {
593 Either::Left(attr) => Some(attr.syntax().clone()),
594 Either::Right(_) => None,
595 })
596 .unwrap_or_else(|| it.syntax().clone())
597 })
598 }
599 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
600 if self.def.is_attribute_derive() {
601 ast_id.with_value(ast_id.to_node(db)).map(|it| {
603 collect_attrs(&it)
604 .nth(invoc_attr_index.ast_index())
605 .and_then(|it| match it.1 {
606 Either::Left(attr) => Some(attr.syntax().clone()),
607 Either::Right(_) => None,
608 })
609 .unwrap_or_else(|| it.syntax().clone())
610 })
611 } else {
612 ast_id.with_value(ast_id.to_node(db).syntax().clone())
613 }
614 }
615 }
616 }
617
618 pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile<ast::Item> {
619 match self.kind {
620 MacroCallKind::FnLike { ast_id, .. } => {
621 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
622 }
623 MacroCallKind::Derive { ast_id, .. } => {
624 InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
625 }
626 MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
627 }
628 }
629
630 fn expand_to(&self) -> ExpandTo {
631 match self.kind {
632 MacroCallKind::FnLike { expand_to, .. } => expand_to,
633 MacroCallKind::Derive { .. } => ExpandTo::Items,
634 MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
635 MacroCallKind::Attr { .. } => {
636 ExpandTo::Items
638 }
639 }
640 }
641
642 pub fn include_file_id(
643 &self,
644 db: &dyn ExpandDatabase,
645 macro_call_id: MacroCallId,
646 ) -> Option<EditionedFileId> {
647 if self.def.is_include() {
648 if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind {
649 if let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg) {
650 return Some(it);
651 }
652 }
653 }
654
655 None
656 }
657}
658
659impl MacroCallKind {
660 pub fn descr(&self) -> &'static str {
661 match self {
662 MacroCallKind::FnLike { .. } => "macro call",
663 MacroCallKind::Derive { .. } => "derive macro",
664 MacroCallKind::Attr { .. } => "attribute macro",
665 }
666 }
667
668 pub fn file_id(&self) -> HirFileId {
670 match *self {
671 MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
672 | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
673 | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
674 }
675 }
676
677 pub fn erased_ast_id(&self) -> ErasedFileAstId {
678 match *self {
679 MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
680 MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
681 MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
682 }
683 }
684
685 pub fn original_call_range_with_body(self, db: &dyn ExpandDatabase) -> FileRange {
689 let mut kind = self;
690 let file_id = loop {
691 match kind.file_id() {
692 HirFileId::MacroFile(file) => {
693 kind = db.lookup_intern_macro_call(file).kind;
694 }
695 HirFileId::FileId(file_id) => break file_id,
696 }
697 };
698
699 let range = match kind {
700 MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
701 MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(),
702 MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(),
703 };
704
705 FileRange { range, file_id }
706 }
707
708 pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange {
714 let mut kind = self;
715 let file_id = loop {
716 match kind.file_id() {
717 HirFileId::MacroFile(file) => {
718 kind = db.lookup_intern_macro_call(file).kind;
719 }
720 HirFileId::FileId(file_id) => break file_id,
721 }
722 };
723
724 let range = match kind {
725 MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
726 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
727 collect_attrs(&ast_id.to_node(db))
730 .nth(derive_attr_index.ast_index())
731 .expect("missing derive")
732 .1
733 .expect_left("derive is a doc comment?")
734 .syntax()
735 .text_range()
736 }
737 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
739 collect_attrs(&ast_id.to_node(db))
740 .nth(invoc_attr_index.ast_index())
741 .expect("missing attribute")
742 .1
743 .expect_left("attribute macro is a doc comment?")
744 .syntax()
745 .text_range()
746 }
747 };
748
749 FileRange { range, file_id }
750 }
751
752 fn arg(&self, db: &dyn ExpandDatabase) -> InFile<Option<SyntaxNode>> {
753 match self {
754 MacroCallKind::FnLike { ast_id, .. } => {
755 ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
756 }
757 MacroCallKind::Derive { ast_id, .. } => {
758 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
759 }
760 MacroCallKind::Attr { ast_id, .. } => {
761 ast_id.to_in_file_node(db).syntax().cloned().map(Some)
762 }
763 }
764 }
765}
766
767#[derive(Clone, Debug, PartialEq, Eq)]
771pub struct ExpansionInfo {
772 expanded: InMacroFile<SyntaxNode>,
773 arg: InFile<Option<SyntaxNode>>,
775 exp_map: Arc<ExpansionSpanMap>,
776 arg_map: SpanMap,
777 loc: MacroCallLoc,
778}
779
780impl ExpansionInfo {
781 pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
782 self.expanded.clone()
783 }
784
785 pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
786 self.arg.as_ref().map(|it| it.as_ref())
787 }
788
789 pub fn call_file(&self) -> HirFileId {
790 self.arg.file_id
791 }
792
793 pub fn is_attr(&self) -> bool {
794 matches!(
795 self.loc.def.kind,
796 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
797 )
798 }
799
800 pub fn map_range_down_exact(
806 &self,
807 span: Span,
808 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
809 let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
810 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
811 });
812
813 Some(InMacroFile::new(self.expanded.file_id, tokens))
814 }
815
816 pub fn map_range_down(
821 &self,
822 span: Span,
823 ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
824 let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
825 self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
826 });
827
828 Some(InMacroFile::new(self.expanded.file_id, tokens))
829 }
830
831 pub fn span_for_offset(
833 &self,
834 db: &dyn ExpandDatabase,
835 offset: TextSize,
836 ) -> (FileRange, SyntaxContext) {
837 debug_assert!(self.expanded.value.text_range().contains(offset));
838 span_for_offset(db, &self.exp_map, offset)
839 }
840
841 pub fn map_node_range_up(
843 &self,
844 db: &dyn ExpandDatabase,
845 range: TextRange,
846 ) -> Option<(FileRange, SyntaxContext)> {
847 debug_assert!(self.expanded.value.text_range().contains_range(range));
848 map_node_range_up(db, &self.exp_map, range)
849 }
850
851 pub fn map_range_up_once(
853 &self,
854 db: &dyn ExpandDatabase,
855 token: TextRange,
856 ) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
857 debug_assert!(self.expanded.value.text_range().contains_range(token));
858 let span = self.exp_map.span_at(token.start());
859 match &self.arg_map {
860 SpanMap::RealSpanMap(_) => {
861 let file_id = EditionedFileId::from_span(db, span.anchor.file_id).into();
862 let anchor_offset =
863 db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
864 InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
865 }
866 SpanMap::ExpansionSpanMap(arg_map) => {
867 let arg_range = self
868 .arg
869 .value
870 .as_ref()
871 .map_or_else(|| TextRange::empty(TextSize::from(0)), |it| it.text_range());
872 InFile::new(
873 self.arg.file_id,
874 arg_map
875 .ranges_with_span_exact(span)
876 .filter(|(range, _)| range.intersect(arg_range).is_some())
877 .map(TupleExt::head)
878 .collect(),
879 )
880 }
881 }
882 }
883
884 pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo {
885 let _p = tracing::info_span!("ExpansionInfo::new").entered();
886 let loc = db.lookup_intern_macro_call(macro_file);
887
888 let arg_tt = loc.kind.arg(db);
889 let arg_map = db.span_map(arg_tt.file_id);
890
891 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
892 let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
893
894 ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
895 }
896}
897
898pub fn map_node_range_up_rooted(
902 db: &dyn ExpandDatabase,
903 exp_map: &ExpansionSpanMap,
904 range: TextRange,
905) -> Option<FileRange> {
906 let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
907 let Span { range, anchor, ctx: _ } = spans.next()?;
908 let mut start = range.start();
909 let mut end = range.end();
910
911 for span in spans {
912 if span.anchor != anchor {
913 return None;
914 }
915 start = start.min(span.range.start());
916 end = end.max(span.range.end());
917 }
918 let file_id = EditionedFileId::from_span(db, anchor.file_id);
919 let anchor_offset =
920 db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
921 Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset })
922}
923
924pub fn map_node_range_up(
928 db: &dyn ExpandDatabase,
929 exp_map: &ExpansionSpanMap,
930 range: TextRange,
931) -> Option<(FileRange, SyntaxContext)> {
932 let mut spans = exp_map.spans_for_range(range);
933 let Span { range, anchor, ctx } = spans.next()?;
934 let mut start = range.start();
935 let mut end = range.end();
936
937 for span in spans {
938 if span.anchor != anchor || span.ctx != ctx {
939 return None;
940 }
941 start = start.min(span.range.start());
942 end = end.max(span.range.end());
943 }
944 let file_id = EditionedFileId::from_span(db, anchor.file_id);
945 let anchor_offset =
946 db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
947 Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx))
948}
949
950pub fn map_node_range_up_aggregated(
953 db: &dyn ExpandDatabase,
954 exp_map: &ExpansionSpanMap,
955 range: TextRange,
956) -> FxHashMap<(SpanAnchor, SyntaxContext), TextRange> {
957 let mut map = FxHashMap::default();
958 for span in exp_map.spans_for_range(range) {
959 let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range);
960 *range = TextRange::new(
961 range.start().min(span.range.start()),
962 range.end().max(span.range.end()),
963 );
964 }
965 for ((anchor, _), range) in &mut map {
966 let file_id = EditionedFileId::from_span(db, anchor.file_id);
967 let anchor_offset =
968 db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
969 *range += anchor_offset;
970 }
971 map
972}
973
974pub fn span_for_offset(
976 db: &dyn ExpandDatabase,
977 exp_map: &ExpansionSpanMap,
978 offset: TextSize,
979) -> (FileRange, SyntaxContext) {
980 let span = exp_map.span_at(offset);
981 let file_id = EditionedFileId::from_span(db, span.anchor.file_id);
982 let anchor_offset =
983 db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start();
984 (FileRange { file_id, range: span.range + anchor_offset }, span.ctx)
985}
986
987#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1003pub enum ExpandTo {
1004 Statements,
1005 Items,
1006 Pattern,
1007 Type,
1008 Expr,
1009}
1010
1011impl ExpandTo {
1012 pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1013 use syntax::SyntaxKind::*;
1014
1015 let syn = call.syntax();
1016
1017 let parent = match syn.parent() {
1018 Some(it) => it,
1019 None => return ExpandTo::Statements,
1020 };
1021
1022 if parent.kind() == MACRO_EXPR
1025 && parent
1026 .parent()
1027 .is_some_and(|p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1028 {
1029 return ExpandTo::Statements;
1030 }
1031
1032 match parent.kind() {
1033 MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1034 MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1035 MACRO_PAT => ExpandTo::Pattern,
1036 MACRO_TYPE => ExpandTo::Type,
1037
1038 ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1039 | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1040 | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1041 | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1042 | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1043 _ => {
1044 ExpandTo::Items
1046 }
1047 }
1048 }
1049}
1050
1051intern::impl_internable!(ModPath, attrs::AttrInput);
1052
1053#[salsa_macros::interned(no_lifetime, debug)]
1054#[doc(alias = "MacroFileId")]
1055pub struct MacroCallId {
1056 pub loc: MacroCallLoc,
1057}
1058
1059impl From<span::MacroCallId> for MacroCallId {
1060 #[inline]
1061 fn from(value: span::MacroCallId) -> Self {
1062 MacroCallId::from_id(value.0)
1063 }
1064}
1065
1066impl From<MacroCallId> for span::MacroCallId {
1067 #[inline]
1068 fn from(value: MacroCallId) -> span::MacroCallId {
1069 span::MacroCallId(value.as_id())
1070 }
1071}
1072
1073#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
1074pub enum HirFileId {
1075 FileId(EditionedFileId),
1076 MacroFile(MacroCallId),
1077}
1078
1079impl From<EditionedFileId> for HirFileId {
1080 #[inline]
1081 fn from(file_id: EditionedFileId) -> Self {
1082 HirFileId::FileId(file_id)
1083 }
1084}
1085
1086impl From<MacroCallId> for HirFileId {
1087 #[inline]
1088 fn from(file_id: MacroCallId) -> Self {
1089 HirFileId::MacroFile(file_id)
1090 }
1091}
1092
1093impl HirFileId {
1094 #[inline]
1095 pub fn macro_file(self) -> Option<MacroCallId> {
1096 match self {
1097 HirFileId::FileId(_) => None,
1098 HirFileId::MacroFile(it) => Some(it),
1099 }
1100 }
1101
1102 #[inline]
1103 pub fn is_macro(self) -> bool {
1104 matches!(self, HirFileId::MacroFile(_))
1105 }
1106
1107 #[inline]
1108 pub fn file_id(self) -> Option<EditionedFileId> {
1109 match self {
1110 HirFileId::FileId(it) => Some(it),
1111 HirFileId::MacroFile(_) => None,
1112 }
1113 }
1114}
1115
1116impl PartialEq<EditionedFileId> for HirFileId {
1117 fn eq(&self, &other: &EditionedFileId) -> bool {
1118 *self == HirFileId::from(other)
1119 }
1120}
1121impl PartialEq<HirFileId> for EditionedFileId {
1122 fn eq(&self, &other: &HirFileId) -> bool {
1123 other == HirFileId::from(*self)
1124 }
1125}