1 //! `hir_expand` deals with macro expansion.
2 //!
3 //! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
4 //! tree originates not from the text of some `FileId`, but from some macro
5 //! expansion.
6
7 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
8
9 pub mod db;
10 pub mod ast_id_map;
11 pub mod name;
12 pub mod hygiene;
13 pub mod builtin_attr_macro;
14 pub mod builtin_derive_macro;
15 pub mod builtin_fn_macro;
16 pub mod proc_macro;
17 pub mod quote;
18 pub mod eager;
19 pub mod mod_path;
20 pub mod attrs;
21 mod fixup;
22
23 use mbe::TokenMap;
24 pub use mbe::{Origin, ValueResult};
25
26 use ::tt::token_id as tt;
27 use triomphe::Arc;
28
29 use std::{fmt, hash::Hash, iter};
30
31 use base_db::{
32 impl_intern_key,
33 salsa::{self, InternId},
34 CrateId, FileId, FileRange, ProcMacroKind,
35 };
36 use either::Either;
37 use syntax::{
38 algo::{self, skip_trivia_token},
39 ast::{self, AstNode, HasDocComments},
40 Direction, SyntaxNode, SyntaxToken,
41 };
42
43 use crate::{
44 ast_id_map::FileAstId,
45 attrs::AttrId,
46 builtin_attr_macro::BuiltinAttrExpander,
47 builtin_derive_macro::BuiltinDeriveExpander,
48 builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
49 db::TokenExpander,
50 mod_path::ModPath,
51 proc_macro::ProcMacroExpander,
52 };
53
54 pub type ExpandResult<T> = ValueResult<T, ExpandError>;
55
56 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
57 pub enum ExpandError {
58 UnresolvedProcMacro(CrateId),
59 Mbe(mbe::ExpandError),
60 RecursionOverflowPoisoned,
61 Other(Box<Box<str>>),
62 }
63
64 impl ExpandError {
other(msg: impl Into<Box<str>>) -> Self65 pub fn other(msg: impl Into<Box<str>>) -> Self {
66 ExpandError::Other(Box::new(msg.into()))
67 }
68 }
69
70 impl From<mbe::ExpandError> for ExpandError {
from(mbe: mbe::ExpandError) -> Self71 fn from(mbe: mbe::ExpandError) -> Self {
72 Self::Mbe(mbe)
73 }
74 }
75
76 impl fmt::Display for ExpandError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 match self {
79 ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
80 ExpandError::Mbe(it) => it.fmt(f),
81 ExpandError::RecursionOverflowPoisoned => {
82 f.write_str("overflow expanding the original macro")
83 }
84 ExpandError::Other(it) => f.write_str(it),
85 }
86 }
87 }
88
89 /// Input to the analyzer is a set of files, where each file is identified by
90 /// `FileId` and contains source code. However, another source of source code in
91 /// Rust are macros: each macro can be thought of as producing a "temporary
92 /// file". To assign an id to such a file, we use the id of the macro call that
93 /// produced the file. So, a `HirFileId` is either a `FileId` (source code
94 /// written by user), or a `MacroCallId` (source code produced by macro).
95 ///
96 /// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
97 /// containing the call plus the offset of the macro call in the file. Note that
98 /// this is a recursive definition! However, the size_of of `HirFileId` is
99 /// finite (because everything bottoms out at the real `FileId`) and small
100 /// (`MacroCallId` uses the location interning. You can check details here:
101 /// <https://en.wikipedia.org/wiki/String_interning>).
102 ///
103 /// The two variants are encoded in a single u32 which are differentiated by the MSB.
104 /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
105 /// `MacroCallId`.
106 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
107 pub struct HirFileId(u32);
108
109 impl fmt::Debug for HirFileId {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 self.repr().fmt(f)
112 }
113 }
114
115 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116 pub struct MacroFile {
117 pub macro_call_id: MacroCallId,
118 }
119
120 /// `MacroCallId` identifies a particular macro invocation, like
121 /// `println!("Hello, {}", world)`.
122 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
123 pub struct MacroCallId(salsa::InternId);
124 impl_intern_key!(MacroCallId);
125
126 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
127 pub struct MacroCallLoc {
128 pub def: MacroDefId,
129 pub(crate) krate: CrateId,
130 /// Some if `def` is a builtin eager macro.
131 eager: Option<Box<EagerCallInfo>>,
132 pub kind: MacroCallKind,
133 }
134
135 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
136 pub struct MacroDefId {
137 pub krate: CrateId,
138 pub kind: MacroDefKind,
139 pub local_inner: bool,
140 pub allow_internal_unsafe: bool,
141 }
142
143 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
144 pub enum MacroDefKind {
145 Declarative(AstId<ast::Macro>),
146 BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
147 BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
148 BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
149 BuiltInEager(EagerExpander, AstId<ast::Macro>),
150 ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
151 }
152
153 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
154 struct EagerCallInfo {
155 /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
156 arg: Arc<(tt::Subtree, TokenMap)>,
157 /// call id of the eager macro's input file. If this is none, macro call containing this call info
158 /// is an eager macro's input, otherwise it is its output.
159 arg_id: Option<MacroCallId>,
160 error: Option<ExpandError>,
161 }
162
163 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
164 pub enum MacroCallKind {
165 FnLike {
166 ast_id: AstId<ast::MacroCall>,
167 expand_to: ExpandTo,
168 },
169 Derive {
170 ast_id: AstId<ast::Adt>,
171 /// Syntactical index of the invoking `#[derive]` attribute.
172 ///
173 /// Outer attributes are counted first, then inner attributes. This does not support
174 /// out-of-line modules, which may have attributes spread across 2 files!
175 derive_attr_index: AttrId,
176 /// Index of the derive macro in the derive attribute
177 derive_index: u32,
178 },
179 Attr {
180 ast_id: AstId<ast::Item>,
181 attr_args: Arc<(tt::Subtree, mbe::TokenMap)>,
182 /// Syntactical index of the invoking `#[attribute]`.
183 ///
184 /// Outer attributes are counted first, then inner attributes. This does not support
185 /// out-of-line modules, which may have attributes spread across 2 files!
186 invoc_attr_index: AttrId,
187 },
188 }
189
190 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191 enum HirFileIdRepr {
192 FileId(FileId),
193 MacroFile(MacroFile),
194 }
195
196 impl From<FileId> for HirFileId {
from(FileId(id): FileId) -> Self197 fn from(FileId(id): FileId) -> Self {
198 assert!(id < Self::MAX_FILE_ID);
199 HirFileId(id)
200 }
201 }
202
203 impl From<MacroFile> for HirFileId {
204 fn from(MacroFile { macro_call_id: MacroCallId(id) }: MacroFile) -> Self {
205 let id = id.as_u32();
206 assert!(id < Self::MAX_FILE_ID);
207 HirFileId(id | Self::MACRO_FILE_TAG_MASK)
208 }
209 }
210
211 impl HirFileId {
212 const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
213 const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
214
215 /// For macro-expansion files, returns the file original source file the
216 /// expansion originated from.
original_file(self, db: &dyn db::ExpandDatabase) -> FileId217 pub fn original_file(self, db: &dyn db::ExpandDatabase) -> FileId {
218 let mut file_id = self;
219 loop {
220 match file_id.repr() {
221 HirFileIdRepr::FileId(id) => break id,
222 HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
223 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
224 let is_include_expansion = loc.def.is_include()
225 && matches!(
226 loc.eager.as_deref(),
227 Some(EagerCallInfo { arg_id: Some(_), .. })
228 );
229 file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
230 Some(Ok((_, file))) => file.into(),
231 _ => loc.kind.file_id(),
232 }
233 }
234 }
235 }
236 }
237
expansion_level(self, db: &dyn db::ExpandDatabase) -> u32238 pub fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32 {
239 let mut level = 0;
240 let mut curr = self;
241 while let Some(macro_file) = curr.macro_file() {
242 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
243
244 level += 1;
245 curr = loc.kind.file_id();
246 }
247 level
248 }
249
250 /// If this is a macro call, returns the syntax node of the call.
call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>>251 pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
252 let macro_file = self.macro_file()?;
253 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
254 Some(loc.to_node(db))
255 }
256
257 /// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)>258 pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
259 let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
260 loop {
261 match call.file_id.repr() {
262 HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
263 HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
264 call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
265 }
266 }
267 }
268 }
269
270 /// Return expansion information if it is a macro-expansion file
expansion_info(self, db: &dyn db::ExpandDatabase) -> Option<ExpansionInfo>271 pub fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option<ExpansionInfo> {
272 let macro_file = self.macro_file()?;
273 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
274
275 let arg_tt = loc.kind.arg(db)?;
276
277 let macro_def = db.macro_def(loc.def).ok()?;
278 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
279 let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
280 Arc::new((
281 tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
282 Default::default(),
283 Default::default(),
284 ))
285 });
286
287 let def = loc.def.ast_id().left().and_then(|id| {
288 let def_tt = match id.to_node(db) {
289 ast::Macro::MacroRules(mac) => mac.token_tree()?,
290 ast::Macro::MacroDef(_) if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) => {
291 return None
292 }
293 ast::Macro::MacroDef(mac) => mac.body()?,
294 };
295 Some(InFile::new(id.file_id, def_tt))
296 });
297 let attr_input_or_mac_def = def.or_else(|| match loc.kind {
298 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
299 // FIXME: handle `cfg_attr`
300 let tt = ast_id
301 .to_node(db)
302 .doc_comments_and_attrs()
303 .nth(invoc_attr_index.ast_index())
304 .and_then(Either::left)?
305 .token_tree()?;
306 Some(InFile::new(ast_id.file_id, tt))
307 }
308 _ => None,
309 });
310
311 Some(ExpansionInfo {
312 expanded: InFile::new(self, parse.syntax_node()),
313 arg: InFile::new(loc.kind.file_id(), arg_tt),
314 attr_input_or_mac_def,
315 macro_arg_shift: mbe::Shift::new(¯o_arg.0),
316 macro_arg,
317 macro_def,
318 exp_map,
319 })
320 }
321
322 /// Indicate it is macro file generated for builtin derive
is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<ast::Attr>>323 pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<ast::Attr>> {
324 let macro_file = self.macro_file()?;
325 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
326 let attr = match loc.def.kind {
327 MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
328 _ => return None,
329 };
330 Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
331 }
332
is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool333 pub fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
334 match self.macro_file() {
335 Some(macro_file) => {
336 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
337 matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _))
338 }
339 None => false,
340 }
341 }
342
343 /// Return whether this file is an include macro
is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool344 pub fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
345 match self.macro_file() {
346 Some(macro_file) => {
347 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
348 loc.def.is_include()
349 }
350 _ => false,
351 }
352 }
353
is_eager(&self, db: &dyn db::ExpandDatabase) -> bool354 pub fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
355 match self.macro_file() {
356 Some(macro_file) => {
357 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
358 matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
359 }
360 _ => false,
361 }
362 }
363
364 /// Return whether this file is an attr macro
is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool365 pub fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
366 match self.macro_file() {
367 Some(macro_file) => {
368 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
369 matches!(loc.kind, MacroCallKind::Attr { .. })
370 }
371 _ => false,
372 }
373 }
374
375 /// Return whether this file is the pseudo expansion of the derive attribute.
376 /// See [`crate::builtin_attr_macro::derive_attr_expand`].
is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool377 pub fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool {
378 match self.macro_file() {
379 Some(macro_file) => {
380 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
381 loc.def.is_attribute_derive()
382 }
383 None => false,
384 }
385 }
386
387 #[inline]
is_macro(self) -> bool388 pub fn is_macro(self) -> bool {
389 self.0 & Self::MACRO_FILE_TAG_MASK != 0
390 }
391
392 #[inline]
macro_file(self) -> Option<MacroFile>393 pub fn macro_file(self) -> Option<MacroFile> {
394 match self.0 & Self::MACRO_FILE_TAG_MASK {
395 0 => None,
396 _ => Some(MacroFile {
397 macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
398 }),
399 }
400 }
401
402 #[inline]
file_id(self) -> Option<FileId>403 pub fn file_id(self) -> Option<FileId> {
404 match self.0 & Self::MACRO_FILE_TAG_MASK {
405 0 => Some(FileId(self.0)),
406 _ => None,
407 }
408 }
409
repr(self) -> HirFileIdRepr410 fn repr(self) -> HirFileIdRepr {
411 match self.0 & Self::MACRO_FILE_TAG_MASK {
412 0 => HirFileIdRepr::FileId(FileId(self.0)),
413 _ => HirFileIdRepr::MacroFile(MacroFile {
414 macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
415 }),
416 }
417 }
418 }
419
420 impl MacroDefId {
as_lazy_macro( self, db: &dyn db::ExpandDatabase, krate: CrateId, kind: MacroCallKind, ) -> MacroCallId421 pub fn as_lazy_macro(
422 self,
423 db: &dyn db::ExpandDatabase,
424 krate: CrateId,
425 kind: MacroCallKind,
426 ) -> MacroCallId {
427 db.intern_macro_call(MacroCallLoc { def: self, krate, eager: None, kind })
428 }
429
ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>>430 pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
431 let id = match self.kind {
432 MacroDefKind::ProcMacro(.., id) => return Either::Right(id),
433 MacroDefKind::Declarative(id)
434 | MacroDefKind::BuiltIn(_, id)
435 | MacroDefKind::BuiltInAttr(_, id)
436 | MacroDefKind::BuiltInDerive(_, id)
437 | MacroDefKind::BuiltInEager(_, id) => id,
438 };
439 Either::Left(id)
440 }
441
is_proc_macro(&self) -> bool442 pub fn is_proc_macro(&self) -> bool {
443 matches!(self.kind, MacroDefKind::ProcMacro(..))
444 }
445
is_attribute(&self) -> bool446 pub fn is_attribute(&self) -> bool {
447 matches!(
448 self.kind,
449 MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
450 )
451 }
452
is_attribute_derive(&self) -> bool453 pub fn is_attribute_derive(&self) -> bool {
454 matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
455 }
456
is_include(&self) -> bool457 pub fn is_include(&self) -> bool {
458 matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
459 }
460 }
461
462 impl MacroCallLoc {
to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode>463 pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
464 match self.kind {
465 MacroCallKind::FnLike { ast_id, .. } => {
466 ast_id.with_value(ast_id.to_node(db).syntax().clone())
467 }
468 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
469 // FIXME: handle `cfg_attr`
470 ast_id.with_value(ast_id.to_node(db)).map(|it| {
471 it.doc_comments_and_attrs()
472 .nth(derive_attr_index.ast_index())
473 .and_then(|it| match it {
474 Either::Left(attr) => Some(attr.syntax().clone()),
475 Either::Right(_) => None,
476 })
477 .unwrap_or_else(|| it.syntax().clone())
478 })
479 }
480 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
481 if self.def.is_attribute_derive() {
482 // FIXME: handle `cfg_attr`
483 ast_id.with_value(ast_id.to_node(db)).map(|it| {
484 it.doc_comments_and_attrs()
485 .nth(invoc_attr_index.ast_index())
486 .and_then(|it| match it {
487 Either::Left(attr) => Some(attr.syntax().clone()),
488 Either::Right(_) => None,
489 })
490 .unwrap_or_else(|| it.syntax().clone())
491 })
492 } else {
493 ast_id.with_value(ast_id.to_node(db).syntax().clone())
494 }
495 }
496 }
497 }
498
expand_to(&self) -> ExpandTo499 fn expand_to(&self) -> ExpandTo {
500 match self.kind {
501 MacroCallKind::FnLike { expand_to, .. } => expand_to,
502 MacroCallKind::Derive { .. } => ExpandTo::Items,
503 MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
504 MacroCallKind::Attr { .. } => {
505 // is this always correct?
506 ExpandTo::Items
507 }
508 }
509 }
510 }
511
512 // FIXME: attribute indices do not account for nested `cfg_attr`
513
514 impl MacroCallKind {
515 /// Returns the file containing the macro invocation.
file_id(&self) -> HirFileId516 fn file_id(&self) -> HirFileId {
517 match *self {
518 MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
519 | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
520 | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
521 }
522 }
523
524 /// Returns the original file range that best describes the location of this macro call.
525 ///
526 /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
original_call_range_with_body(self, db: &dyn db::ExpandDatabase) -> FileRange527 pub fn original_call_range_with_body(self, db: &dyn db::ExpandDatabase) -> FileRange {
528 let mut kind = self;
529 let file_id = loop {
530 match kind.file_id().repr() {
531 HirFileIdRepr::MacroFile(file) => {
532 kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
533 }
534 HirFileIdRepr::FileId(file_id) => break file_id,
535 }
536 };
537
538 let range = match kind {
539 MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
540 MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
541 MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
542 };
543
544 FileRange { range, file_id }
545 }
546
547 /// Returns the original file range that best describes the location of this macro call.
548 ///
549 /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
550 /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives
551 /// get only the specific derive that is being referred to.
original_call_range(self, db: &dyn db::ExpandDatabase) -> FileRange552 pub fn original_call_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
553 let mut kind = self;
554 let file_id = loop {
555 match kind.file_id().repr() {
556 HirFileIdRepr::MacroFile(file) => {
557 kind = db.lookup_intern_macro_call(file.macro_call_id).kind;
558 }
559 HirFileIdRepr::FileId(file_id) => break file_id,
560 }
561 };
562
563 let range = match kind {
564 MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
565 MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
566 // FIXME: should be the range of the macro name, not the whole derive
567 // FIXME: handle `cfg_attr`
568 ast_id
569 .to_node(db)
570 .doc_comments_and_attrs()
571 .nth(derive_attr_index.ast_index())
572 .expect("missing derive")
573 .expect_left("derive is a doc comment?")
574 .syntax()
575 .text_range()
576 }
577 // FIXME: handle `cfg_attr`
578 MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
579 .to_node(db)
580 .doc_comments_and_attrs()
581 .nth(invoc_attr_index.ast_index())
582 .expect("missing attribute")
583 .expect_left("attribute macro is a doc comment?")
584 .syntax()
585 .text_range(),
586 };
587
588 FileRange { range, file_id }
589 }
590
arg(&self, db: &dyn db::ExpandDatabase) -> Option<SyntaxNode>591 fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<SyntaxNode> {
592 match self {
593 MacroCallKind::FnLike { ast_id, .. } => {
594 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
595 }
596 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
597 MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
598 }
599 }
600 }
601
602 impl MacroCallId {
as_file(self) -> HirFileId603 pub fn as_file(self) -> HirFileId {
604 MacroFile { macro_call_id: self }.into()
605 }
606
as_macro_file(self) -> MacroFile607 pub fn as_macro_file(self) -> MacroFile {
608 MacroFile { macro_call_id: self }
609 }
610 }
611
612 /// ExpansionInfo mainly describes how to map text range between src and expanded macro
613 #[derive(Debug, Clone, PartialEq, Eq)]
614 pub struct ExpansionInfo {
615 expanded: InFile<SyntaxNode>,
616 /// The argument TokenTree or item for attributes
617 arg: InFile<SyntaxNode>,
618 /// The `macro_rules!` or attribute input.
619 attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
620
621 macro_def: Arc<TokenExpander>,
622 macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
623 /// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
624 /// and as such we need to shift tokens if they are part of an attributes input instead of their item.
625 macro_arg_shift: mbe::Shift,
626 exp_map: Arc<mbe::TokenMap>,
627 }
628
629 impl ExpansionInfo {
expanded(&self) -> InFile<SyntaxNode>630 pub fn expanded(&self) -> InFile<SyntaxNode> {
631 self.expanded.clone()
632 }
633
call_node(&self) -> Option<InFile<SyntaxNode>>634 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
635 Some(self.arg.with_value(self.arg.value.parent()?))
636 }
637
638 /// Map a token down from macro input into the macro expansion.
639 ///
640 /// The inner workings of this function differ slightly depending on the type of macro we are dealing with:
641 /// - declarative:
642 /// For declarative macros, we need to accommodate for the macro definition site(which acts as a second unchanging input)
643 /// , as tokens can mapped in and out of it.
644 /// To do this we shift all ids in the expansion by the maximum id of the definition site giving us an easy
645 /// way to map all the tokens.
646 /// - attribute:
647 /// Attributes have two different inputs, the input tokentree in the attribute node and the item
648 /// the attribute is annotating. Similarly as for declarative macros we need to do a shift here
649 /// as well. Currently this is done by shifting the attribute input by the maximum id of the item.
650 /// - function-like and derives:
651 /// Both of these only have one simple call site input so no special handling is required here.
map_token_down( &self, db: &dyn db::ExpandDatabase, item: Option<ast::Item>, token: InFile<&SyntaxToken>, ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_>652 pub fn map_token_down(
653 &self,
654 db: &dyn db::ExpandDatabase,
655 item: Option<ast::Item>,
656 token: InFile<&SyntaxToken>,
657 ) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
658 assert_eq!(token.file_id, self.arg.file_id);
659 let token_id_in_attr_input = if let Some(item) = item {
660 // check if we are mapping down in an attribute input
661 // this is a special case as attributes can have two inputs
662 let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
663 let loc = db.lookup_intern_macro_call(call_id);
664
665 let token_range = token.value.text_range();
666 match &loc.kind {
667 MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
668 // FIXME: handle `cfg_attr`
669 let attr = item
670 .doc_comments_and_attrs()
671 .nth(invoc_attr_index.ast_index())
672 .and_then(Either::left)?;
673 match attr.token_tree() {
674 Some(token_tree)
675 if token_tree.syntax().text_range().contains_range(token_range) =>
676 {
677 let attr_input_start =
678 token_tree.left_delimiter_token()?.text_range().start();
679 let relative_range =
680 token.value.text_range().checked_sub(attr_input_start)?;
681 // shift by the item's tree's max id
682 let token_id = attr_args.1.token_by_range(relative_range)?;
683
684 let token_id = if loc.def.is_attribute_derive() {
685 // we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
686 token_id
687 } else {
688 self.macro_arg_shift.shift(token_id)
689 };
690 Some(token_id)
691 }
692 _ => None,
693 }
694 }
695 _ => None,
696 }
697 } else {
698 None
699 };
700
701 let token_id = match token_id_in_attr_input {
702 Some(token_id) => token_id,
703 // the token is not inside `an attribute's input so do the lookup in the macro_arg as usual
704 None => {
705 let relative_range =
706 token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
707 let token_id = self.macro_arg.1.token_by_range(relative_range)?;
708 // conditionally shift the id by a declaratives macro definition
709 self.macro_def.map_id_down(token_id)
710 }
711 };
712
713 let tokens = self
714 .exp_map
715 .ranges_by_token(token_id, token.value.kind())
716 .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
717
718 Some(tokens.map(move |token| self.expanded.with_value(token)))
719 }
720
721 /// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
map_token_up( &self, db: &dyn db::ExpandDatabase, token: InFile<&SyntaxToken>, ) -> Option<(InFile<SyntaxToken>, Origin)>722 pub fn map_token_up(
723 &self,
724 db: &dyn db::ExpandDatabase,
725 token: InFile<&SyntaxToken>,
726 ) -> Option<(InFile<SyntaxToken>, Origin)> {
727 // Fetch the id through its text range,
728 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
729 // conditionally unshifting the id to accommodate for macro-rules def site
730 let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
731
732 let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
733 let loc = db.lookup_intern_macro_call(call_id);
734
735 // Special case: map tokens from `include!` expansions to the included file
736 if loc.def.is_include()
737 && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
738 {
739 if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
740 let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
741 let source = db.parse(file_id);
742
743 let token = source.syntax_node().covering_element(range).into_token()?;
744
745 return Some((InFile::new(file_id.into(), token), Origin::Call));
746 }
747 }
748
749 // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
750 let (token_map, tt) = match &loc.kind {
751 MacroCallKind::Attr { attr_args, .. } => {
752 if loc.def.is_attribute_derive() {
753 (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
754 } else {
755 // try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
756 // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
757 match self.macro_arg_shift.unshift(token_id) {
758 Some(unshifted) => {
759 token_id = unshifted;
760 (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
761 }
762 None => (&self.macro_arg.1, self.arg.clone()),
763 }
764 }
765 }
766 _ => match origin {
767 mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
768 mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) {
769 (TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
770 (def_site_token_map, tt.syntax().cloned())
771 }
772 _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
773 },
774 },
775 };
776
777 let range = token_map.first_range_by_token(token_id, token.value.kind())?;
778 let token =
779 tt.value.covering_element(range + tt.value.text_range().start()).into_token()?;
780 Some((tt.with_value(token), origin))
781 }
782 }
783
784 /// `AstId` points to an AST node in any file.
785 ///
786 /// It is stable across reparses, and can be used as salsa key/value.
787 pub type AstId<N> = InFile<FileAstId<N>>;
788
789 impl<N: AstNode> AstId<N> {
to_node(&self, db: &dyn db::ExpandDatabase) -> N790 pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
791 let root = db.parse_or_expand(self.file_id);
792 db.ast_id_map(self.file_id).get(self.value).to_node(&root)
793 }
794 }
795
796 /// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
797 ///
798 /// Typical usages are:
799 ///
800 /// * `InFile<SyntaxNode>` -- syntax node in a file
801 /// * `InFile<ast::FnDef>` -- ast node in a file
802 /// * `InFile<TextSize>` -- offset in a file
803 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
804 pub struct InFile<T> {
805 pub file_id: HirFileId,
806 pub value: T,
807 }
808
809 impl<T> InFile<T> {
new(file_id: HirFileId, value: T) -> InFile<T>810 pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
811 InFile { file_id, value }
812 }
813
with_value<U>(&self, value: U) -> InFile<U>814 pub fn with_value<U>(&self, value: U) -> InFile<U> {
815 InFile::new(self.file_id, value)
816 }
817
map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U>818 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
819 InFile::new(self.file_id, f(self.value))
820 }
821
as_ref(&self) -> InFile<&T>822 pub fn as_ref(&self) -> InFile<&T> {
823 self.with_value(&self.value)
824 }
825
file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode826 pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
827 db.parse_or_expand(self.file_id)
828 }
829 }
830
831 impl<T: Clone> InFile<&T> {
cloned(&self) -> InFile<T>832 pub fn cloned(&self) -> InFile<T> {
833 self.with_value(self.value.clone())
834 }
835 }
836
837 impl<T> InFile<Option<T>> {
transpose(self) -> Option<InFile<T>>838 pub fn transpose(self) -> Option<InFile<T>> {
839 let value = self.value?;
840 Some(InFile::new(self.file_id, value))
841 }
842 }
843
844 impl<L, R> InFile<Either<L, R>> {
transpose(self) -> Either<InFile<L>, InFile<R>>845 pub fn transpose(self) -> Either<InFile<L>, InFile<R>> {
846 match self.value {
847 Either::Left(l) => Either::Left(InFile::new(self.file_id, l)),
848 Either::Right(r) => Either::Right(InFile::new(self.file_id, r)),
849 }
850 }
851 }
852
853 impl<'a> InFile<&'a SyntaxNode> {
ancestors_with_macros( self, db: &dyn db::ExpandDatabase, ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_854 pub fn ancestors_with_macros(
855 self,
856 db: &dyn db::ExpandDatabase,
857 ) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
858 iter::successors(Some(self.cloned()), move |node| match node.value.parent() {
859 Some(parent) => Some(node.with_value(parent)),
860 None => node.file_id.call_node(db),
861 })
862 }
863
864 /// Skips the attributed item that caused the macro invocation we are climbing up
ancestors_with_macros_skip_attr_item( self, db: &dyn db::ExpandDatabase, ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_865 pub fn ancestors_with_macros_skip_attr_item(
866 self,
867 db: &dyn db::ExpandDatabase,
868 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
869 let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
870 Some(parent) => Some(node.with_value(parent)),
871 None => {
872 let parent_node = node.file_id.call_node(db)?;
873 if node.file_id.is_attr_macro(db) {
874 // macro call was an attributed item, skip it
875 // FIXME: does this fail if this is a direct expansion of another macro?
876 parent_node.map(|node| node.parent()).transpose()
877 } else {
878 Some(parent_node)
879 }
880 }
881 };
882 iter::successors(succ(&self.cloned()), succ)
883 }
884
885 /// Falls back to the macro call range if the node cannot be mapped up fully.
886 ///
887 /// For attributes and derives, this will point back to the attribute only.
888 /// For the entire item use [`InFile::original_file_range_full`].
original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange889 pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
890 match self.file_id.repr() {
891 HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
892 HirFileIdRepr::MacroFile(mac_file) => {
893 if let Some(res) = self.original_file_range_opt(db) {
894 return res;
895 }
896 // Fall back to whole macro call.
897 let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
898 loc.kind.original_call_range(db)
899 }
900 }
901 }
902
903 /// Falls back to the macro call range if the node cannot be mapped up fully.
original_file_range_full(self, db: &dyn db::ExpandDatabase) -> FileRange904 pub fn original_file_range_full(self, db: &dyn db::ExpandDatabase) -> FileRange {
905 match self.file_id.repr() {
906 HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
907 HirFileIdRepr::MacroFile(mac_file) => {
908 if let Some(res) = self.original_file_range_opt(db) {
909 return res;
910 }
911 // Fall back to whole macro call.
912 let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
913 loc.kind.original_call_range_with_body(db)
914 }
915 }
916 }
917
918 /// Attempts to map the syntax node back up its macro calls.
original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange>919 pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
920 match ascend_node_border_tokens(db, self) {
921 Some(InFile { file_id, value: (first, last) }) => {
922 let original_file = file_id.original_file(db);
923 let range = first.text_range().cover(last.text_range());
924 if file_id != original_file.into() {
925 tracing::error!("Failed mapping up more for {:?}", range);
926 return None;
927 }
928 Some(FileRange { file_id: original_file, range })
929 }
930 _ if !self.file_id.is_macro() => Some(FileRange {
931 file_id: self.file_id.original_file(db),
932 range: self.value.text_range(),
933 }),
934 _ => None,
935 }
936 }
937
original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>>938 pub fn original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
939 // This kind of upmapping can only be achieved in attribute expanded files,
940 // as we don't have node inputs otherwise and therefore can't find an `N` node in the input
941 if !self.file_id.is_macro() {
942 return Some(self.map(Clone::clone));
943 } else if !self.file_id.is_attr_macro(db) {
944 return None;
945 }
946
947 if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
948 {
949 if file_id.is_macro() {
950 let range = first.text_range().cover(last.text_range());
951 tracing::error!("Failed mapping out of macro file for {:?}", range);
952 return None;
953 }
954 // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
955 let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
956 let kind = self.value.kind();
957 let value = anc.ancestors().find(|it| it.kind() == kind)?;
958 return Some(InFile::new(file_id, value));
959 }
960 None
961 }
962 }
963
964 impl InFile<SyntaxToken> {
upmap(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxToken>>965 pub fn upmap(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxToken>> {
966 let expansion = self.file_id.expansion_info(db)?;
967 expansion.map_token_up(db, self.as_ref()).map(|(it, _)| it)
968 }
969
970 /// Falls back to the macro call range if the node cannot be mapped up fully.
original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange971 pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
972 match self.file_id.repr() {
973 HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
974 HirFileIdRepr::MacroFile(mac_file) => {
975 if let Some(res) = self.original_file_range_opt(db) {
976 return res;
977 }
978 // Fall back to whole macro call.
979 let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
980 loc.kind.original_call_range(db)
981 }
982 }
983 }
984
985 /// Attempts to map the syntax node back up its macro calls.
original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange>986 pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
987 match self.file_id.repr() {
988 HirFileIdRepr::FileId(file_id) => {
989 Some(FileRange { file_id, range: self.value.text_range() })
990 }
991 HirFileIdRepr::MacroFile(_) => {
992 let expansion = self.file_id.expansion_info(db)?;
993 let InFile { file_id, value } = ascend_call_token(db, &expansion, self)?;
994 let original_file = file_id.original_file(db);
995 if file_id != original_file.into() {
996 return None;
997 }
998 Some(FileRange { file_id: original_file, range: value.text_range() })
999 }
1000 }
1001 }
1002
ancestors_with_macros( self, db: &dyn db::ExpandDatabase, ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_1003 pub fn ancestors_with_macros(
1004 self,
1005 db: &dyn db::ExpandDatabase,
1006 ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
1007 self.value.parent().into_iter().flat_map({
1008 let file_id = self.file_id;
1009 move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
1010 })
1011 }
1012 }
1013
1014 fn ascend_node_border_tokens(
1015 db: &dyn db::ExpandDatabase,
1016 InFile { file_id, value: node }: InFile<&SyntaxNode>,
1017 ) -> Option<InFile<(SyntaxToken, SyntaxToken)>> {
1018 let expansion = file_id.expansion_info(db)?;
1019
1020 let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
1021 let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
1022
1023 // FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
1024 let first = first_token(node)?;
1025 let last = last_token(node)?;
1026 let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
1027 let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
1028 (first.file_id == last.file_id).then(|| InFile::new(first.file_id, (first.value, last.value)))
1029 }
1030
ascend_call_token( db: &dyn db::ExpandDatabase, expansion: &ExpansionInfo, token: InFile<SyntaxToken>, ) -> Option<InFile<SyntaxToken>>1031 fn ascend_call_token(
1032 db: &dyn db::ExpandDatabase,
1033 expansion: &ExpansionInfo,
1034 token: InFile<SyntaxToken>,
1035 ) -> Option<InFile<SyntaxToken>> {
1036 let mut mapping = expansion.map_token_up(db, token.as_ref())?;
1037 while let (mapped, Origin::Call) = mapping {
1038 match mapped.file_id.expansion_info(db) {
1039 Some(info) => mapping = info.map_token_up(db, mapped.as_ref())?,
1040 None => return Some(mapped),
1041 }
1042 }
1043 None
1044 }
1045
1046 impl<N: AstNode> InFile<N> {
descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>>1047 pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> {
1048 self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
1049 }
1050
1051 // FIXME: this should return `Option<InFileNotHirFile<N>>`
original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>>1052 pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>> {
1053 // This kind of upmapping can only be achieved in attribute expanded files,
1054 // as we don't have node inputs otherwise and therefore can't find an `N` node in the input
1055 if !self.file_id.is_macro() {
1056 return Some(self);
1057 } else if !self.file_id.is_attr_macro(db) {
1058 return None;
1059 }
1060
1061 if let Some(InFile { file_id, value: (first, last) }) =
1062 ascend_node_border_tokens(db, self.syntax())
1063 {
1064 if file_id.is_macro() {
1065 let range = first.text_range().cover(last.text_range());
1066 tracing::error!("Failed mapping out of macro file for {:?}", range);
1067 return None;
1068 }
1069 // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
1070 let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
1071 let value = anc.ancestors().find_map(N::cast)?;
1072 return Some(InFile::new(file_id, value));
1073 }
1074 None
1075 }
1076
syntax(&self) -> InFile<&SyntaxNode>1077 pub fn syntax(&self) -> InFile<&SyntaxNode> {
1078 self.with_value(self.value.syntax())
1079 }
1080 }
1081
1082 /// In Rust, macros expand token trees to token trees. When we want to turn a
1083 /// token tree into an AST node, we need to figure out what kind of AST node we
1084 /// want: something like `foo` can be a type, an expression, or a pattern.
1085 ///
1086 /// Naively, one would think that "what this expands to" is a property of a
1087 /// particular macro: macro `m1` returns an item, while macro `m2` returns an
1088 /// expression, etc. That's not the case -- macros are polymorphic in the
1089 /// result, and can expand to any type of the AST node.
1090 ///
1091 /// What defines the actual AST node is the syntactic context of the macro
1092 /// invocation. As a contrived example, in `let T![*] = T![*];` the first `T`
1093 /// expands to a pattern, while the second one expands to an expression.
1094 ///
1095 /// `ExpandTo` captures this bit of information about a particular macro call
1096 /// site.
1097 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1098 pub enum ExpandTo {
1099 Statements,
1100 Items,
1101 Pattern,
1102 Type,
1103 Expr,
1104 }
1105
1106 impl ExpandTo {
from_call_site(call: &ast::MacroCall) -> ExpandTo1107 pub fn from_call_site(call: &ast::MacroCall) -> ExpandTo {
1108 use syntax::SyntaxKind::*;
1109
1110 let syn = call.syntax();
1111
1112 let parent = match syn.parent() {
1113 Some(it) => it,
1114 None => return ExpandTo::Statements,
1115 };
1116
1117 // FIXME: macros in statement position are treated as expression statements, they should
1118 // probably be their own statement kind. The *grand*parent indicates what's valid.
1119 if parent.kind() == MACRO_EXPR
1120 && parent
1121 .parent()
1122 .map_or(false, |p| matches!(p.kind(), EXPR_STMT | STMT_LIST | MACRO_STMTS))
1123 {
1124 return ExpandTo::Statements;
1125 }
1126
1127 match parent.kind() {
1128 MACRO_ITEMS | SOURCE_FILE | ITEM_LIST => ExpandTo::Items,
1129 MACRO_STMTS | EXPR_STMT | STMT_LIST => ExpandTo::Statements,
1130 MACRO_PAT => ExpandTo::Pattern,
1131 MACRO_TYPE => ExpandTo::Type,
1132
1133 ARG_LIST | ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR
1134 | CLOSURE_EXPR | FIELD_EXPR | FOR_EXPR | IF_EXPR | INDEX_EXPR | LET_EXPR
1135 | MATCH_ARM | MATCH_EXPR | MATCH_GUARD | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR
1136 | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR_FIELD | REF_EXPR | RETURN_EXPR | TRY_EXPR
1137 | TUPLE_EXPR | WHILE_EXPR | MACRO_EXPR => ExpandTo::Expr,
1138 _ => {
1139 // Unknown , Just guess it is `Items`
1140 ExpandTo::Items
1141 }
1142 }
1143 }
1144 }
1145
1146 #[derive(Debug)]
1147 pub struct UnresolvedMacro {
1148 pub path: ModPath,
1149 }
1150
1151 intern::impl_internable!(ModPath, attrs::AttrInput);
1152