1 //! Machinery for hygienic macros, inspired by the `MTWT[1]` paper.
3 //! `[1]` Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler. 2012.
4 //! *Macros that work together: Compile-time bindings, partial expansion,
5 //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
6 //! DOI=10.1017/S0956796812000093 <https://doi.org/10.1017/S0956796812000093>
8 // Hygiene data is stored in a global variable and accessed via TLS, which
9 // means that accesses are somewhat expensive. (`HygieneData::with`
10 // encapsulates a single access.) Therefore, on hot code paths it is worth
11 // ensuring that multiple HygieneData accesses are combined into a single
12 // `HygieneData::with`.
14 // This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces
15 // with a certain amount of redundancy in them. For example,
16 // `SyntaxContext::outer_expn_info` combines `SyntaxContext::outer` and
17 // `ExpnId::expn_info` so that two `HygieneData` accesses can be performed within
18 // a single `HygieneData::with` call.
20 // It also explains why many functions appear in `HygieneData` and again in
21 // `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and
22 // `SyntaxContext::outer` do the same thing, but the former is for use within a
23 // `HygieneData::with` call while the latter is for use outside such a call.
24 // When modifying this file it is important to understand this distinction,
25 // because getting it wrong can lead to nested `HygieneData::with` calls that
26 // trigger runtime aborts. (Fortunately these are obvious and easy to fix.)
29 use crate::{Span, DUMMY_SP};
30 use crate::edition::Edition;
31 use crate::symbol::{kw, Symbol};
33 use serialize::{Encodable, Decodable, Encoder, Decoder};
34 use rustc_data_structures::fx::FxHashMap;
35 use rustc_data_structures::sync::Lrc;
38 /// A SyntaxContext represents a chain of macro expansions (represented by marks).
39 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40 pub struct SyntaxContext(u32);
43 struct SyntaxContextData {
45 transparency: Transparency,
46 prev_ctxt: SyntaxContext,
47 /// This context, but with all transparent and semi-transparent marks filtered away.
48 opaque: SyntaxContext,
49 /// This context, but with all transparent marks filtered away.
50 opaque_and_semitransparent: SyntaxContext,
51 /// Name of the crate to which `$crate` with this context would resolve.
52 dollar_crate_name: Symbol,
55 /// A mark is a unique ID associated with a macro expansion.
56 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
57 pub struct ExpnId(u32);
62 /// Each mark should have an associated expansion info, but sometimes there's a delay between
63 /// creation of a mark and obtaining its info (e.g. macros are collected first and then
64 /// resolved later), so we use an `Option` here.
65 expn_info: Option<ExpnInfo>,
68 /// A property of a macro expansion that determines how identifiers
69 /// produced by that expansion are resolved.
70 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, RustcEncodable, RustcDecodable)]
71 pub enum Transparency {
72 /// Identifier produced by a transparent expansion is always resolved at call-site.
73 /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
75 /// Identifier produced by a semi-transparent expansion may be resolved
76 /// either at call-site or at definition-site.
77 /// If it's a local variable, label or `$crate` then it's resolved at def-site.
78 /// Otherwise it's resolved at call-site.
79 /// `macro_rules` macros behave like this, built-in macros currently behave like this too,
80 /// but that's an implementation detail.
82 /// Identifier produced by an opaque expansion is always resolved at definition-site.
83 /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
88 pub fn fresh(parent: ExpnId, expn_info: Option<ExpnInfo>) -> Self {
89 HygieneData::with(|data| data.fresh_mark(parent, expn_info))
92 /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
94 pub fn root() -> Self {
99 pub fn as_u32(self) -> u32 {
104 pub fn from_u32(raw: u32) -> ExpnId {
109 pub fn parent(self) -> ExpnId {
110 HygieneData::with(|data| data.marks[self.0 as usize].parent)
114 pub fn expn_info(self) -> Option<ExpnInfo> {
115 HygieneData::with(|data| data.expn_info(self).cloned())
119 pub fn set_expn_info(self, info: ExpnInfo) {
120 HygieneData::with(|data| {
121 let old_info = &mut data.marks[self.0 as usize].expn_info;
122 assert!(old_info.is_none(), "expansion info is reset for a mark");
123 *old_info = Some(info);
127 pub fn is_descendant_of(self, ancestor: ExpnId) -> bool {
128 HygieneData::with(|data| data.is_descendant_of(self, ancestor))
131 /// `mark.outer_is_descendant_of(ctxt)` is equivalent to but faster than
132 /// `mark.is_descendant_of(ctxt.outer())`.
133 pub fn outer_is_descendant_of(self, ctxt: SyntaxContext) -> bool {
134 HygieneData::with(|data| data.is_descendant_of(self, data.outer(ctxt)))
137 // Used for enabling some compatibility fallback in resolve.
139 pub fn looks_like_proc_macro_derive(self) -> bool {
140 HygieneData::with(|data| {
141 if data.default_transparency(self) == Transparency::Opaque {
142 if let Some(expn_info) = data.expn_info(self) {
143 if let ExpnKind::Macro(MacroKind::Derive, _) = expn_info.kind {
154 crate struct HygieneData {
155 marks: Vec<MarkData>,
156 syntax_contexts: Vec<SyntaxContextData>,
157 markings: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>,
161 crate fn new(edition: Edition) -> Self {
163 marks: vec![MarkData {
164 parent: ExpnId::root(),
165 expn_info: Some(ExpnInfo::default(ExpnKind::Root, DUMMY_SP, edition)),
167 syntax_contexts: vec![SyntaxContextData {
168 outer_mark: ExpnId::root(),
169 transparency: Transparency::Opaque,
170 prev_ctxt: SyntaxContext(0),
171 opaque: SyntaxContext(0),
172 opaque_and_semitransparent: SyntaxContext(0),
173 dollar_crate_name: kw::DollarCrate,
175 markings: FxHashMap::default(),
179 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
180 GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut()))
183 fn fresh_mark(&mut self, parent: ExpnId, expn_info: Option<ExpnInfo>) -> ExpnId {
184 self.marks.push(MarkData { parent, expn_info });
185 ExpnId(self.marks.len() as u32 - 1)
188 fn expn_info(&self, mark: ExpnId) -> Option<&ExpnInfo> {
189 if mark != ExpnId::root() {
190 Some(self.marks[mark.0 as usize].expn_info.as_ref()
191 .expect("no expansion info for a mark"))
193 // FIXME: Some code relies on `expn_info().is_none()` meaning "no expansion".
194 // Introduce a method for checking for "no expansion" instead and always return
195 // `ExpnInfo` from this function instead of the `Option`.
200 fn is_descendant_of(&self, mut mark: ExpnId, ancestor: ExpnId) -> bool {
201 while mark != ancestor {
202 if mark == ExpnId::root() {
205 mark = self.marks[mark.0 as usize].parent;
210 fn default_transparency(&self, mark: ExpnId) -> Transparency {
211 self.expn_info(mark).map_or(
212 Transparency::SemiTransparent, |einfo| einfo.default_transparency
216 fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext {
217 self.syntax_contexts[ctxt.0 as usize].opaque
220 fn modern_and_legacy(&self, ctxt: SyntaxContext) -> SyntaxContext {
221 self.syntax_contexts[ctxt.0 as usize].opaque_and_semitransparent
224 fn outer(&self, ctxt: SyntaxContext) -> ExpnId {
225 self.syntax_contexts[ctxt.0 as usize].outer_mark
228 fn transparency(&self, ctxt: SyntaxContext) -> Transparency {
229 self.syntax_contexts[ctxt.0 as usize].transparency
232 fn prev_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext {
233 self.syntax_contexts[ctxt.0 as usize].prev_ctxt
236 fn remove_mark(&self, ctxt: &mut SyntaxContext) -> ExpnId {
237 let outer_mark = self.syntax_contexts[ctxt.0 as usize].outer_mark;
238 *ctxt = self.prev_ctxt(*ctxt);
242 fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> {
243 let mut marks = Vec::new();
244 while ctxt != SyntaxContext::empty() {
245 let outer_mark = self.outer(ctxt);
246 let transparency = self.transparency(ctxt);
247 let prev_ctxt = self.prev_ctxt(ctxt);
248 marks.push((outer_mark, transparency));
255 fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
256 while span.ctxt() != crate::NO_EXPANSION && span.ctxt() != to {
257 if let Some(info) = self.expn_info(self.outer(span.ctxt())) {
258 span = info.call_site;
266 fn adjust(&self, ctxt: &mut SyntaxContext, expansion: ExpnId) -> Option<ExpnId> {
267 let mut scope = None;
268 while !self.is_descendant_of(expansion, self.outer(*ctxt)) {
269 scope = Some(self.remove_mark(ctxt));
274 fn apply_mark(&mut self, ctxt: SyntaxContext, mark: ExpnId) -> SyntaxContext {
275 assert_ne!(mark, ExpnId::root());
276 self.apply_mark_with_transparency(ctxt, mark, self.default_transparency(mark))
279 fn apply_mark_with_transparency(&mut self, ctxt: SyntaxContext, mark: ExpnId,
280 transparency: Transparency) -> SyntaxContext {
281 assert_ne!(mark, ExpnId::root());
282 if transparency == Transparency::Opaque {
283 return self.apply_mark_internal(ctxt, mark, transparency);
287 self.expn_info(mark).map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
288 let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
289 self.modern(call_site_ctxt)
291 self.modern_and_legacy(call_site_ctxt)
294 if call_site_ctxt == SyntaxContext::empty() {
295 return self.apply_mark_internal(ctxt, mark, transparency);
298 // Otherwise, `mark` is a macros 1.0 definition and the call site is in a
299 // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition.
301 // In this case, the tokens from the macros 1.0 definition inherit the hygiene
302 // at their invocation. That is, we pretend that the macros 1.0 definition
303 // was defined at its invocation (i.e., inside the macros 2.0 definition)
304 // so that the macros 2.0 definition remains hygienic.
306 // See the example at `test/run-pass/hygiene/legacy_interaction.rs`.
307 for (mark, transparency) in self.marks(ctxt) {
308 call_site_ctxt = self.apply_mark_internal(call_site_ctxt, mark, transparency);
310 self.apply_mark_internal(call_site_ctxt, mark, transparency)
313 fn apply_mark_internal(&mut self, ctxt: SyntaxContext, mark: ExpnId, transparency: Transparency)
315 let syntax_contexts = &mut self.syntax_contexts;
316 let mut opaque = syntax_contexts[ctxt.0 as usize].opaque;
317 let mut opaque_and_semitransparent =
318 syntax_contexts[ctxt.0 as usize].opaque_and_semitransparent;
320 if transparency >= Transparency::Opaque {
321 let prev_ctxt = opaque;
322 opaque = *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
323 let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
324 syntax_contexts.push(SyntaxContextData {
329 opaque_and_semitransparent: new_opaque,
330 dollar_crate_name: kw::DollarCrate,
336 if transparency >= Transparency::SemiTransparent {
337 let prev_ctxt = opaque_and_semitransparent;
338 opaque_and_semitransparent =
339 *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
340 let new_opaque_and_semitransparent =
341 SyntaxContext(syntax_contexts.len() as u32);
342 syntax_contexts.push(SyntaxContextData {
347 opaque_and_semitransparent: new_opaque_and_semitransparent,
348 dollar_crate_name: kw::DollarCrate,
350 new_opaque_and_semitransparent
354 let prev_ctxt = ctxt;
355 *self.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
356 let new_opaque_and_semitransparent_and_transparent =
357 SyntaxContext(syntax_contexts.len() as u32);
358 syntax_contexts.push(SyntaxContextData {
363 opaque_and_semitransparent,
364 dollar_crate_name: kw::DollarCrate,
366 new_opaque_and_semitransparent_and_transparent
371 pub fn clear_markings() {
372 HygieneData::with(|data| data.markings = FxHashMap::default());
375 pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
376 HygieneData::with(|data| data.walk_chain(span, to))
379 pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
380 // The new contexts that need updating are at the end of the list and have `$crate` as a name.
381 let (len, to_update) = HygieneData::with(|data| (
382 data.syntax_contexts.len(),
383 data.syntax_contexts.iter().rev()
384 .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate).count()
386 // The callback must be called from outside of the `HygieneData` lock,
387 // since it will try to acquire it too.
388 let range_to_update = len - to_update .. len;
390 range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect();
391 HygieneData::with(|data| range_to_update.zip(names.into_iter()).for_each(|(idx, name)| {
392 data.syntax_contexts[idx].dollar_crate_name = name;
398 pub const fn empty() -> Self {
403 crate fn as_u32(self) -> u32 {
408 crate fn from_u32(raw: u32) -> SyntaxContext {
412 /// Extend a syntax context with a given mark and default transparency for that mark.
413 pub fn apply_mark(self, mark: ExpnId) -> SyntaxContext {
414 HygieneData::with(|data| data.apply_mark(self, mark))
417 /// Extend a syntax context with a given mark and transparency
418 pub fn apply_mark_with_transparency(self, mark: ExpnId, transparency: Transparency)
420 HygieneData::with(|data| data.apply_mark_with_transparency(self, mark, transparency))
423 /// Pulls a single mark off of the syntax context. This effectively moves the
424 /// context up one macro definition level. That is, if we have a nested macro
425 /// definition as follows:
435 /// and we have a SyntaxContext that is referring to something declared by an invocation
436 /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the
437 /// invocation of f that created g1.
438 /// Returns the mark that was removed.
439 pub fn remove_mark(&mut self) -> ExpnId {
440 HygieneData::with(|data| data.remove_mark(self))
443 pub fn marks(self) -> Vec<(ExpnId, Transparency)> {
444 HygieneData::with(|data| data.marks(self))
447 /// Adjust this context for resolution in a scope created by the given expansion.
448 /// For example, consider the following three resolutions of `f`:
451 /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty.
453 /// macro m($f:ident) {
455 /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`.
456 /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty.
458 /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m`
459 /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`,
460 /// //| and it resolves to `::foo::f`.
461 /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m`
462 /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`,
463 /// //| and it resolves to `::bar::f`.
464 /// bar::$f(); // `f`'s `SyntaxContext` is empty.
465 /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`,
466 /// //| and it resolves to `::bar::$f`.
469 /// This returns the expansion whose definition scope we use to privacy check the resolution,
470 /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope).
471 pub fn adjust(&mut self, expansion: ExpnId) -> Option<ExpnId> {
472 HygieneData::with(|data| data.adjust(self, expansion))
475 /// Like `SyntaxContext::adjust`, but also modernizes `self`.
476 pub fn modernize_and_adjust(&mut self, expansion: ExpnId) -> Option<ExpnId> {
477 HygieneData::with(|data| {
478 *self = data.modern(*self);
479 data.adjust(self, expansion)
483 /// Adjust this context for resolution in a scope created by the given expansion
484 /// via a glob import with the given `SyntaxContext`.
489 /// macro m($i:ident) {
491 /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`.
492 /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
495 /// macro n($j:ident) {
497 /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
498 /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`.
499 /// $i(); // `$i`'s `SyntaxContext` has a mark from `n`
500 /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`.
501 /// $j(); // `$j`'s `SyntaxContext` has a mark from `m`
502 /// //^ This cannot be glob-adjusted, so this is a resolution error.
506 /// This returns `None` if the context cannot be glob-adjusted.
507 /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details).
508 pub fn glob_adjust(&mut self, expansion: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> {
509 HygieneData::with(|data| {
510 let mut scope = None;
511 let mut glob_ctxt = data.modern(glob_span.ctxt());
512 while !data.is_descendant_of(expansion, data.outer(glob_ctxt)) {
513 scope = Some(data.remove_mark(&mut glob_ctxt));
514 if data.remove_mark(self) != scope.unwrap() {
518 if data.adjust(self, expansion).is_some() {
525 /// Undo `glob_adjust` if possible:
528 /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) {
529 /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope));
532 pub fn reverse_glob_adjust(&mut self, expansion: ExpnId, glob_span: Span)
533 -> Option<Option<ExpnId>> {
534 HygieneData::with(|data| {
535 if data.adjust(self, expansion).is_some() {
539 let mut glob_ctxt = data.modern(glob_span.ctxt());
540 let mut marks = Vec::new();
541 while !data.is_descendant_of(expansion, data.outer(glob_ctxt)) {
542 marks.push(data.remove_mark(&mut glob_ctxt));
545 let scope = marks.last().cloned();
546 while let Some(mark) = marks.pop() {
547 *self = data.apply_mark(*self, mark);
553 pub fn hygienic_eq(self, other: SyntaxContext, mark: ExpnId) -> bool {
554 HygieneData::with(|data| {
555 let mut self_modern = data.modern(self);
556 data.adjust(&mut self_modern, mark);
557 self_modern == data.modern(other)
562 pub fn modern(self) -> SyntaxContext {
563 HygieneData::with(|data| data.modern(self))
567 pub fn modern_and_legacy(self) -> SyntaxContext {
568 HygieneData::with(|data| data.modern_and_legacy(self))
572 pub fn outer(self) -> ExpnId {
573 HygieneData::with(|data| data.outer(self))
576 /// `ctxt.outer_expn_info()` is equivalent to but faster than
577 /// `ctxt.outer().expn_info()`.
579 pub fn outer_expn_info(self) -> Option<ExpnInfo> {
580 HygieneData::with(|data| data.expn_info(data.outer(self)).cloned())
583 /// `ctxt.outer_and_expn_info()` is equivalent to but faster than
584 /// `{ let outer = ctxt.outer(); (outer, outer.expn_info()) }`.
586 pub fn outer_and_expn_info(self) -> (ExpnId, Option<ExpnInfo>) {
587 HygieneData::with(|data| {
588 let outer = data.outer(self);
589 (outer, data.expn_info(outer).cloned())
593 pub fn dollar_crate_name(self) -> Symbol {
594 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
598 impl fmt::Debug for SyntaxContext {
599 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600 write!(f, "#{}", self.0)
605 /// Creates a fresh expansion with given properties.
606 /// Expansions are normally created by macros, but in some cases expansions are created for
607 /// other compiler-generated code to set per-span properties like allowed unstable features.
608 /// The returned span belongs to the created expansion and has the new properties,
609 /// but its location is inherited from the current span.
610 pub fn fresh_expansion(self, parent: ExpnId, expn_info: ExpnInfo) -> Span {
611 HygieneData::with(|data| {
612 let mark = data.fresh_mark(parent, Some(expn_info));
613 self.with_ctxt(data.apply_mark(SyntaxContext::empty(), mark))
618 /// A subset of properties from both macro definition and macro call available through global data.
619 /// Avoid using this if you have access to the original definition or call structures.
620 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
621 pub struct ExpnInfo {
622 // --- The part unique to each expansion.
623 /// The location of the actual macro invocation or syntax sugar , e.g.
624 /// `let x = foo!();` or `if let Some(y) = x {}`
626 /// This may recursively refer to other macro invocations, e.g., if
627 /// `foo!()` invoked `bar!()` internally, and there was an
628 /// expression inside `bar!`; the call_site of the expression in
629 /// the expansion would point to the `bar!` invocation; that
630 /// call_site span would have its own ExpnInfo, with the call_site
631 /// pointing to the `foo!` invocation.
633 /// The kind of this expansion - macro or compiler desugaring.
636 // --- The part specific to the macro/desugaring definition.
637 // --- FIXME: Share it between expansions with the same definition.
638 /// The span of the macro definition (possibly dummy).
639 /// This span serves only informational purpose and is not used for resolution.
641 /// Transparency used by `apply_mark` for mark with this expansion info by default.
642 pub default_transparency: Transparency,
643 /// List of #[unstable]/feature-gated features that the macro is allowed to use
644 /// internally without forcing the whole crate to opt-in
646 pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
647 /// Whether the macro is allowed to use `unsafe` internally
648 /// even if the user crate has `#![forbid(unsafe_code)]`.
649 pub allow_internal_unsafe: bool,
650 /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`)
651 /// for a given macro.
652 pub local_inner_macros: bool,
653 /// Edition of the crate in which the macro is defined.
654 pub edition: Edition,
658 /// Constructs an expansion info with default properties.
659 pub fn default(kind: ExpnKind, call_site: Span, edition: Edition) -> ExpnInfo {
664 default_transparency: Transparency::SemiTransparent,
665 allow_internal_unstable: None,
666 allow_internal_unsafe: false,
667 local_inner_macros: false,
672 pub fn allow_unstable(kind: ExpnKind, call_site: Span, edition: Edition,
673 allow_internal_unstable: Lrc<[Symbol]>) -> ExpnInfo {
675 allow_internal_unstable: Some(allow_internal_unstable),
676 ..ExpnInfo::default(kind, call_site, edition)
682 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
684 /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind.
686 /// Expansion produced by a macro.
687 /// FIXME: Some code injected by the compiler before HIR lowering also gets this kind.
688 Macro(MacroKind, Symbol),
689 /// Desugaring done by the compiler during HIR lowering.
690 Desugaring(DesugaringKind)
694 pub fn descr(&self) -> Symbol {
696 ExpnKind::Root => kw::PathRoot,
697 ExpnKind::Macro(_, descr) => descr,
698 ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()),
703 /// The kind of macro invocation or definition.
704 #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
706 /// A bang macro `foo!()`.
708 /// An attribute macro `#[foo]`.
710 /// A derive macro `#[derive(Foo)]`
715 pub fn descr(self) -> &'static str {
717 MacroKind::Bang => "macro",
718 MacroKind::Attr => "attribute macro",
719 MacroKind::Derive => "derive macro",
723 pub fn article(self) -> &'static str {
725 MacroKind::Attr => "an",
731 /// The kind of compiler desugaring.
732 #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
733 pub enum DesugaringKind {
734 /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`.
735 /// However, we do not want to blame `c` for unreachability but rather say that `i`
736 /// is unreachable. This desugaring kind allows us to avoid blaming `c`.
737 /// This also applies to `while` loops.
741 /// Desugaring of an `impl Trait` in return type position
742 /// to an `existential type Foo: Trait;` and replacing the
743 /// `impl Trait` with `Foo`.
750 impl DesugaringKind {
751 /// The description wording should combine well with "desugaring of {}".
752 fn descr(self) -> &'static str {
754 DesugaringKind::CondTemporary => "`if` or `while` condition",
755 DesugaringKind::Async => "`async` block or function",
756 DesugaringKind::Await => "`await` expression",
757 DesugaringKind::QuestionMark => "operator `?`",
758 DesugaringKind::TryBlock => "`try` block",
759 DesugaringKind::ExistentialType => "`existential type`",
760 DesugaringKind::ForLoop => "`for` loop",
765 impl Encodable for SyntaxContext {
766 fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
767 Ok(()) // FIXME(jseyfried) intercrate hygiene
771 impl Decodable for SyntaxContext {
772 fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
773 Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene