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