]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_pos/hygiene.rs
Remove recommendation about idiomatic syntax for Arc::Clone
[rust.git] / src / libsyntax_pos / hygiene.rs
1 //! Machinery for hygienic macros, inspired by the `MTWT[1]` paper.
2 //!
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>
7
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`.
13 //
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.
19 //
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.)
27
28 use crate::GLOBALS;
29 use crate::{Span, DUMMY_SP};
30 use crate::edition::Edition;
31 use crate::symbol::{kw, Symbol};
32
33 use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
34 use rustc_data_structures::fx::FxHashMap;
35 use rustc_data_structures::sync::Lrc;
36 use std::fmt;
37
38 /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
39 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
40 pub struct SyntaxContext(u32);
41
42 #[derive(Debug)]
43 struct SyntaxContextData {
44     outer_expn: ExpnId,
45     outer_transparency: Transparency,
46     parent: SyntaxContext,
47     /// This context, but with all transparent and semi-transparent expansions filtered away.
48     opaque: SyntaxContext,
49     /// This context, but with all transparent expansions 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,
53 }
54
55 /// A unique ID associated with a macro invocation and expansion.
56 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
57 pub struct ExpnId(u32);
58
59 // FIXME: Find a way to merge this with `ExpnInfo`.
60 #[derive(Debug)]
61 struct InternalExpnData {
62     parent: ExpnId,
63     /// Each expansion should have an associated expansion info, but sometimes there's a delay
64     /// between creation of an expansion ID and obtaining its info (e.g. macros are collected
65     /// first and then resolved later), so we use an `Option` here.
66     expn_info: Option<ExpnInfo>,
67 }
68
69 /// A property of a macro expansion that determines how identifiers
70 /// produced by that expansion are resolved.
71 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, RustcEncodable, RustcDecodable)]
72 pub enum Transparency {
73     /// Identifier produced by a transparent expansion is always resolved at call-site.
74     /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
75     Transparent,
76     /// Identifier produced by a semi-transparent expansion may be resolved
77     /// either at call-site or at definition-site.
78     /// If it's a local variable, label or `$crate` then it's resolved at def-site.
79     /// Otherwise it's resolved at call-site.
80     /// `macro_rules` macros behave like this, built-in macros currently behave like this too,
81     /// but that's an implementation detail.
82     SemiTransparent,
83     /// Identifier produced by an opaque expansion is always resolved at definition-site.
84     /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
85     Opaque,
86 }
87
88 impl ExpnId {
89     pub fn fresh(parent: ExpnId, expn_info: Option<ExpnInfo>) -> Self {
90         HygieneData::with(|data| data.fresh_expn(parent, expn_info))
91     }
92
93     /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
94     #[inline]
95     pub fn root() -> Self {
96         ExpnId(0)
97     }
98
99     #[inline]
100     pub fn as_u32(self) -> u32 {
101         self.0
102     }
103
104     #[inline]
105     pub fn from_u32(raw: u32) -> ExpnId {
106         ExpnId(raw)
107     }
108
109     #[inline]
110     pub fn parent(self) -> ExpnId {
111         HygieneData::with(|data| data.parent_expn(self))
112     }
113
114     #[inline]
115     pub fn expn_info(self) -> Option<ExpnInfo> {
116         HygieneData::with(|data| data.expn_info(self).cloned())
117     }
118
119     #[inline]
120     pub fn set_expn_info(self, info: ExpnInfo) {
121         HygieneData::with(|data| {
122             let old_info = &mut data.expn_data[self.0 as usize].expn_info;
123             assert!(old_info.is_none(), "expansion info is reset for an expansion ID");
124             *old_info = Some(info);
125         })
126     }
127
128     pub fn is_descendant_of(self, ancestor: ExpnId) -> bool {
129         HygieneData::with(|data| data.is_descendant_of(self, ancestor))
130     }
131
132     /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than
133     /// `expn_id.is_descendant_of(ctxt.outer_expn())`.
134     pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool {
135         HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt)))
136     }
137
138     // Used for enabling some compatibility fallback in resolve.
139     #[inline]
140     pub fn looks_like_proc_macro_derive(self) -> bool {
141         HygieneData::with(|data| {
142             if data.default_transparency(self) == Transparency::Opaque {
143                 if let Some(expn_info) = data.expn_info(self) {
144                     if let ExpnKind::Macro(MacroKind::Derive, _) = expn_info.kind {
145                         return true;
146                     }
147                 }
148             }
149             false
150         })
151     }
152 }
153
154 #[derive(Debug)]
155 crate struct HygieneData {
156     expn_data: Vec<InternalExpnData>,
157     syntax_context_data: Vec<SyntaxContextData>,
158     syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>,
159 }
160
161 impl HygieneData {
162     crate fn new(edition: Edition) -> Self {
163         HygieneData {
164             expn_data: vec![InternalExpnData {
165                 parent: ExpnId::root(),
166                 expn_info: Some(ExpnInfo::default(ExpnKind::Root, DUMMY_SP, edition)),
167             }],
168             syntax_context_data: vec![SyntaxContextData {
169                 outer_expn: ExpnId::root(),
170                 outer_transparency: Transparency::Opaque,
171                 parent: SyntaxContext(0),
172                 opaque: SyntaxContext(0),
173                 opaque_and_semitransparent: SyntaxContext(0),
174                 dollar_crate_name: kw::DollarCrate,
175             }],
176             syntax_context_map: FxHashMap::default(),
177         }
178     }
179
180     fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
181         GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut()))
182     }
183
184     fn fresh_expn(&mut self, parent: ExpnId, expn_info: Option<ExpnInfo>) -> ExpnId {
185         self.expn_data.push(InternalExpnData { parent, expn_info });
186         ExpnId(self.expn_data.len() as u32 - 1)
187     }
188
189     fn parent_expn(&self, expn_id: ExpnId) -> ExpnId {
190         self.expn_data[expn_id.0 as usize].parent
191     }
192
193     fn expn_info(&self, expn_id: ExpnId) -> Option<&ExpnInfo> {
194         if expn_id != ExpnId::root() {
195             Some(self.expn_data[expn_id.0 as usize].expn_info.as_ref()
196                      .expect("no expansion info for an expansion ID"))
197         } else {
198             // FIXME: Some code relies on `expn_info().is_none()` meaning "no expansion".
199             // Introduce a method for checking for "no expansion" instead and always return
200             // `ExpnInfo` from this function instead of the `Option`.
201             None
202         }
203     }
204
205     fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool {
206         while expn_id != ancestor {
207             if expn_id == ExpnId::root() {
208                 return false;
209             }
210             expn_id = self.parent_expn(expn_id);
211         }
212         true
213     }
214
215     fn default_transparency(&self, expn_id: ExpnId) -> Transparency {
216         self.expn_info(expn_id).map_or(
217             Transparency::SemiTransparent, |einfo| einfo.default_transparency
218         )
219     }
220
221     fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext {
222         self.syntax_context_data[ctxt.0 as usize].opaque
223     }
224
225     fn modern_and_legacy(&self, ctxt: SyntaxContext) -> SyntaxContext {
226         self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent
227     }
228
229     fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
230         self.syntax_context_data[ctxt.0 as usize].outer_expn
231     }
232
233     fn outer_transparency(&self, ctxt: SyntaxContext) -> Transparency {
234         self.syntax_context_data[ctxt.0 as usize].outer_transparency
235     }
236
237     fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext {
238         self.syntax_context_data[ctxt.0 as usize].parent
239     }
240
241     fn remove_mark(&self, ctxt: &mut SyntaxContext) -> ExpnId {
242         let outer_expn = self.outer_expn(*ctxt);
243         *ctxt = self.parent_ctxt(*ctxt);
244         outer_expn
245     }
246
247     fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> {
248         let mut marks = Vec::new();
249         while ctxt != SyntaxContext::empty() {
250             marks.push((self.outer_expn(ctxt), self.outer_transparency(ctxt)));
251             ctxt = self.parent_ctxt(ctxt);
252         }
253         marks.reverse();
254         marks
255     }
256
257     fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
258         while span.ctxt() != crate::NO_EXPANSION && span.ctxt() != to {
259             if let Some(info) = self.expn_info(self.outer_expn(span.ctxt())) {
260                 span = info.call_site;
261             } else {
262                 break;
263             }
264         }
265         span
266     }
267
268     fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> {
269         let mut scope = None;
270         while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) {
271             scope = Some(self.remove_mark(ctxt));
272         }
273         scope
274     }
275
276     fn apply_mark(&mut self, ctxt: SyntaxContext, expn_id: ExpnId) -> SyntaxContext {
277         assert_ne!(expn_id, ExpnId::root());
278         self.apply_mark_with_transparency(ctxt, expn_id, self.default_transparency(expn_id))
279     }
280
281     fn apply_mark_with_transparency(&mut self, ctxt: SyntaxContext, expn_id: ExpnId,
282                                     transparency: Transparency) -> SyntaxContext {
283         assert_ne!(expn_id, ExpnId::root());
284         if transparency == Transparency::Opaque {
285             return self.apply_mark_internal(ctxt, expn_id, transparency);
286         }
287
288         let call_site_ctxt =
289             self.expn_info(expn_id).map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
290         let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
291             self.modern(call_site_ctxt)
292         } else {
293             self.modern_and_legacy(call_site_ctxt)
294         };
295
296         if call_site_ctxt == SyntaxContext::empty() {
297             return self.apply_mark_internal(ctxt, expn_id, transparency);
298         }
299
300         // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
301         // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition.
302         //
303         // In this case, the tokens from the macros 1.0 definition inherit the hygiene
304         // at their invocation. That is, we pretend that the macros 1.0 definition
305         // was defined at its invocation (i.e., inside the macros 2.0 definition)
306         // so that the macros 2.0 definition remains hygienic.
307         //
308         // See the example at `test/ui/hygiene/legacy_interaction.rs`.
309         for (expn_id, transparency) in self.marks(ctxt) {
310             call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency);
311         }
312         self.apply_mark_internal(call_site_ctxt, expn_id, transparency)
313     }
314
315     fn apply_mark_internal(
316         &mut self, ctxt: SyntaxContext, expn_id: ExpnId, transparency: Transparency
317     ) -> SyntaxContext {
318         let syntax_context_data = &mut self.syntax_context_data;
319         let mut opaque = syntax_context_data[ctxt.0 as usize].opaque;
320         let mut opaque_and_semitransparent =
321             syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent;
322
323         if transparency >= Transparency::Opaque {
324             let parent = opaque;
325             opaque = *self.syntax_context_map.entry((parent, expn_id, transparency))
326                                              .or_insert_with(|| {
327                 let new_opaque = SyntaxContext(syntax_context_data.len() as u32);
328                 syntax_context_data.push(SyntaxContextData {
329                     outer_expn: expn_id,
330                     outer_transparency: transparency,
331                     parent,
332                     opaque: new_opaque,
333                     opaque_and_semitransparent: new_opaque,
334                     dollar_crate_name: kw::DollarCrate,
335                 });
336                 new_opaque
337             });
338         }
339
340         if transparency >= Transparency::SemiTransparent {
341             let parent = opaque_and_semitransparent;
342             opaque_and_semitransparent =
343                     *self.syntax_context_map.entry((parent, expn_id, transparency))
344                                             .or_insert_with(|| {
345                 let new_opaque_and_semitransparent =
346                     SyntaxContext(syntax_context_data.len() as u32);
347                 syntax_context_data.push(SyntaxContextData {
348                     outer_expn: expn_id,
349                     outer_transparency: transparency,
350                     parent,
351                     opaque,
352                     opaque_and_semitransparent: new_opaque_and_semitransparent,
353                     dollar_crate_name: kw::DollarCrate,
354                 });
355                 new_opaque_and_semitransparent
356             });
357         }
358
359         let parent = ctxt;
360         *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| {
361             let new_opaque_and_semitransparent_and_transparent =
362                 SyntaxContext(syntax_context_data.len() as u32);
363             syntax_context_data.push(SyntaxContextData {
364                 outer_expn: expn_id,
365                 outer_transparency: transparency,
366                 parent,
367                 opaque,
368                 opaque_and_semitransparent,
369                 dollar_crate_name: kw::DollarCrate,
370             });
371             new_opaque_and_semitransparent_and_transparent
372         })
373     }
374 }
375
376 pub fn clear_syntax_context_map() {
377     HygieneData::with(|data| data.syntax_context_map = FxHashMap::default());
378 }
379
380 pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
381     HygieneData::with(|data| data.walk_chain(span, to))
382 }
383
384 pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
385     // The new contexts that need updating are at the end of the list and have `$crate` as a name.
386     let (len, to_update) = HygieneData::with(|data| (
387         data.syntax_context_data.len(),
388         data.syntax_context_data.iter().rev()
389             .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate).count()
390     ));
391     // The callback must be called from outside of the `HygieneData` lock,
392     // since it will try to acquire it too.
393     let range_to_update = len - to_update .. len;
394     let names: Vec<_> =
395         range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect();
396     HygieneData::with(|data| range_to_update.zip(names.into_iter()).for_each(|(idx, name)| {
397         data.syntax_context_data[idx].dollar_crate_name = name;
398     }))
399 }
400
401 impl SyntaxContext {
402     #[inline]
403     pub const fn empty() -> Self {
404         SyntaxContext(0)
405     }
406
407     #[inline]
408     crate fn as_u32(self) -> u32 {
409         self.0
410     }
411
412     #[inline]
413     crate fn from_u32(raw: u32) -> SyntaxContext {
414         SyntaxContext(raw)
415     }
416
417     /// Extend a syntax context with a given expansion and default transparency for that expansion.
418     pub fn apply_mark(self, expn_id: ExpnId) -> SyntaxContext {
419         HygieneData::with(|data| data.apply_mark(self, expn_id))
420     }
421
422     /// Extend a syntax context with a given expansion and transparency.
423     pub fn apply_mark_with_transparency(self, expn_id: ExpnId, transparency: Transparency)
424                                         -> SyntaxContext {
425         HygieneData::with(|data| data.apply_mark_with_transparency(self, expn_id, transparency))
426     }
427
428     /// Pulls a single mark off of the syntax context. This effectively moves the
429     /// context up one macro definition level. That is, if we have a nested macro
430     /// definition as follows:
431     ///
432     /// ```rust
433     /// macro_rules! f {
434     ///    macro_rules! g {
435     ///        ...
436     ///    }
437     /// }
438     /// ```
439     ///
440     /// and we have a SyntaxContext that is referring to something declared by an invocation
441     /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the
442     /// invocation of f that created g1.
443     /// Returns the mark that was removed.
444     pub fn remove_mark(&mut self) -> ExpnId {
445         HygieneData::with(|data| data.remove_mark(self))
446     }
447
448     pub fn marks(self) -> Vec<(ExpnId, Transparency)> {
449         HygieneData::with(|data| data.marks(self))
450     }
451
452     /// Adjust this context for resolution in a scope created by the given expansion.
453     /// For example, consider the following three resolutions of `f`:
454     ///
455     /// ```rust
456     /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty.
457     /// m!(f);
458     /// macro m($f:ident) {
459     ///     mod bar {
460     ///         pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`.
461     ///         pub fn $f() {} // `$f`'s `SyntaxContext` is empty.
462     ///     }
463     ///     foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m`
464     ///     //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`,
465     ///     //| and it resolves to `::foo::f`.
466     ///     bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m`
467     ///     //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`,
468     ///     //| and it resolves to `::bar::f`.
469     ///     bar::$f(); // `f`'s `SyntaxContext` is empty.
470     ///     //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`,
471     ///     //| and it resolves to `::bar::$f`.
472     /// }
473     /// ```
474     /// This returns the expansion whose definition scope we use to privacy check the resolution,
475     /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope).
476     pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> {
477         HygieneData::with(|data| data.adjust(self, expn_id))
478     }
479
480     /// Like `SyntaxContext::adjust`, but also modernizes `self`.
481     pub fn modernize_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> {
482         HygieneData::with(|data| {
483             *self = data.modern(*self);
484             data.adjust(self, expn_id)
485         })
486     }
487
488     /// Adjust this context for resolution in a scope created by the given expansion
489     /// via a glob import with the given `SyntaxContext`.
490     /// For example:
491     ///
492     /// ```rust
493     /// m!(f);
494     /// macro m($i:ident) {
495     ///     mod foo {
496     ///         pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`.
497     ///         pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
498     ///     }
499     ///     n(f);
500     ///     macro n($j:ident) {
501     ///         use foo::*;
502     ///         f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
503     ///         //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`.
504     ///         $i(); // `$i`'s `SyntaxContext` has a mark from `n`
505     ///         //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`.
506     ///         $j(); // `$j`'s `SyntaxContext` has a mark from `m`
507     ///         //^ This cannot be glob-adjusted, so this is a resolution error.
508     ///     }
509     /// }
510     /// ```
511     /// This returns `None` if the context cannot be glob-adjusted.
512     /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details).
513     pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> {
514         HygieneData::with(|data| {
515             let mut scope = None;
516             let mut glob_ctxt = data.modern(glob_span.ctxt());
517             while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) {
518                 scope = Some(data.remove_mark(&mut glob_ctxt));
519                 if data.remove_mark(self) != scope.unwrap() {
520                     return None;
521                 }
522             }
523             if data.adjust(self, expn_id).is_some() {
524                 return None;
525             }
526             Some(scope)
527         })
528     }
529
530     /// Undo `glob_adjust` if possible:
531     ///
532     /// ```rust
533     /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) {
534     ///     assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope));
535     /// }
536     /// ```
537     pub fn reverse_glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span)
538                                -> Option<Option<ExpnId>> {
539         HygieneData::with(|data| {
540             if data.adjust(self, expn_id).is_some() {
541                 return None;
542             }
543
544             let mut glob_ctxt = data.modern(glob_span.ctxt());
545             let mut marks = Vec::new();
546             while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) {
547                 marks.push(data.remove_mark(&mut glob_ctxt));
548             }
549
550             let scope = marks.last().cloned();
551             while let Some(mark) = marks.pop() {
552                 *self = data.apply_mark(*self, mark);
553             }
554             Some(scope)
555         })
556     }
557
558     pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool {
559         HygieneData::with(|data| {
560             let mut self_modern = data.modern(self);
561             data.adjust(&mut self_modern, expn_id);
562             self_modern == data.modern(other)
563         })
564     }
565
566     #[inline]
567     pub fn modern(self) -> SyntaxContext {
568         HygieneData::with(|data| data.modern(self))
569     }
570
571     #[inline]
572     pub fn modern_and_legacy(self) -> SyntaxContext {
573         HygieneData::with(|data| data.modern_and_legacy(self))
574     }
575
576     #[inline]
577     pub fn outer_expn(self) -> ExpnId {
578         HygieneData::with(|data| data.outer_expn(self))
579     }
580
581     /// `ctxt.outer_expn_info()` is equivalent to but faster than
582     /// `ctxt.outer_expn().expn_info()`.
583     #[inline]
584     pub fn outer_expn_info(self) -> Option<ExpnInfo> {
585         HygieneData::with(|data| data.expn_info(data.outer_expn(self)).cloned())
586     }
587
588     /// `ctxt.outer_expn_with_info()` is equivalent to but faster than
589     /// `{ let outer = ctxt.outer_expn(); (outer, outer.expn_info()) }`.
590     #[inline]
591     pub fn outer_expn_with_info(self) -> (ExpnId, Option<ExpnInfo>) {
592         HygieneData::with(|data| {
593             let outer = data.outer_expn(self);
594             (outer, data.expn_info(outer).cloned())
595         })
596     }
597
598     pub fn dollar_crate_name(self) -> Symbol {
599         HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name)
600     }
601 }
602
603 impl fmt::Debug for SyntaxContext {
604     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605         write!(f, "#{}", self.0)
606     }
607 }
608
609 impl Span {
610     /// Creates a fresh expansion with given properties.
611     /// Expansions are normally created by macros, but in some cases expansions are created for
612     /// other compiler-generated code to set per-span properties like allowed unstable features.
613     /// The returned span belongs to the created expansion and has the new properties,
614     /// but its location is inherited from the current span.
615     pub fn fresh_expansion(self, parent: ExpnId, expn_info: ExpnInfo) -> Span {
616         HygieneData::with(|data| {
617             let expn_id = data.fresh_expn(parent, Some(expn_info));
618             self.with_ctxt(data.apply_mark(SyntaxContext::empty(), expn_id))
619         })
620     }
621 }
622
623 /// A subset of properties from both macro definition and macro call available through global data.
624 /// Avoid using this if you have access to the original definition or call structures.
625 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
626 pub struct ExpnInfo {
627     // --- The part unique to each expansion.
628     /// The location of the actual macro invocation or syntax sugar , e.g.
629     /// `let x = foo!();` or `if let Some(y) = x {}`
630     ///
631     /// This may recursively refer to other macro invocations, e.g., if
632     /// `foo!()` invoked `bar!()` internally, and there was an
633     /// expression inside `bar!`; the call_site of the expression in
634     /// the expansion would point to the `bar!` invocation; that
635     /// call_site span would have its own ExpnInfo, with the call_site
636     /// pointing to the `foo!` invocation.
637     pub call_site: Span,
638     /// The kind of this expansion - macro or compiler desugaring.
639     pub kind: ExpnKind,
640
641     // --- The part specific to the macro/desugaring definition.
642     // --- FIXME: Share it between expansions with the same definition.
643     /// The span of the macro definition (possibly dummy).
644     /// This span serves only informational purpose and is not used for resolution.
645     pub def_site: Span,
646     /// Transparency used by `apply_mark` for the expansion with this expansion info by default.
647     pub default_transparency: Transparency,
648     /// List of #[unstable]/feature-gated features that the macro is allowed to use
649     /// internally without forcing the whole crate to opt-in
650     /// to them.
651     pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
652     /// Whether the macro is allowed to use `unsafe` internally
653     /// even if the user crate has `#![forbid(unsafe_code)]`.
654     pub allow_internal_unsafe: bool,
655     /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`)
656     /// for a given macro.
657     pub local_inner_macros: bool,
658     /// Edition of the crate in which the macro is defined.
659     pub edition: Edition,
660 }
661
662 impl ExpnInfo {
663     /// Constructs an expansion info with default properties.
664     pub fn default(kind: ExpnKind, call_site: Span, edition: Edition) -> ExpnInfo {
665         ExpnInfo {
666             call_site,
667             kind,
668             def_site: DUMMY_SP,
669             default_transparency: Transparency::SemiTransparent,
670             allow_internal_unstable: None,
671             allow_internal_unsafe: false,
672             local_inner_macros: false,
673             edition,
674         }
675     }
676
677     pub fn allow_unstable(kind: ExpnKind, call_site: Span, edition: Edition,
678                           allow_internal_unstable: Lrc<[Symbol]>) -> ExpnInfo {
679         ExpnInfo {
680             allow_internal_unstable: Some(allow_internal_unstable),
681             ..ExpnInfo::default(kind, call_site, edition)
682         }
683     }
684 }
685
686 /// Expansion kind.
687 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
688 pub enum ExpnKind {
689     /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind.
690     Root,
691     /// Expansion produced by a macro.
692     /// FIXME: Some code injected by the compiler before HIR lowering also gets this kind.
693     Macro(MacroKind, Symbol),
694     /// Desugaring done by the compiler during HIR lowering.
695     Desugaring(DesugaringKind)
696 }
697
698 impl ExpnKind {
699     pub fn descr(&self) -> Symbol {
700         match *self {
701             ExpnKind::Root => kw::PathRoot,
702             ExpnKind::Macro(_, descr) => descr,
703             ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()),
704         }
705     }
706 }
707
708 /// The kind of macro invocation or definition.
709 #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
710 pub enum MacroKind {
711     /// A bang macro `foo!()`.
712     Bang,
713     /// An attribute macro `#[foo]`.
714     Attr,
715     /// A derive macro `#[derive(Foo)]`
716     Derive,
717 }
718
719 impl MacroKind {
720     pub fn descr(self) -> &'static str {
721         match self {
722             MacroKind::Bang => "macro",
723             MacroKind::Attr => "attribute macro",
724             MacroKind::Derive => "derive macro",
725         }
726     }
727
728     pub fn article(self) -> &'static str {
729         match self {
730             MacroKind::Attr => "an",
731             _ => "a",
732         }
733     }
734 }
735
736 /// The kind of compiler desugaring.
737 #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
738 pub enum DesugaringKind {
739     /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`.
740     /// However, we do not want to blame `c` for unreachability but rather say that `i`
741     /// is unreachable. This desugaring kind allows us to avoid blaming `c`.
742     /// This also applies to `while` loops.
743     CondTemporary,
744     QuestionMark,
745     TryBlock,
746     /// Desugaring of an `impl Trait` in return type position
747     /// to an `type Foo = impl Trait;` and replacing the
748     /// `impl Trait` with `Foo`.
749     OpaqueTy,
750     Async,
751     Await,
752     ForLoop,
753 }
754
755 impl DesugaringKind {
756     /// The description wording should combine well with "desugaring of {}".
757     fn descr(self) -> &'static str {
758         match self {
759             DesugaringKind::CondTemporary => "`if` or `while` condition",
760             DesugaringKind::Async => "`async` block or function",
761             DesugaringKind::Await => "`await` expression",
762             DesugaringKind::QuestionMark => "operator `?`",
763             DesugaringKind::TryBlock => "`try` block",
764             DesugaringKind::OpaqueTy => "`impl Trait`",
765             DesugaringKind::ForLoop => "`for` loop",
766         }
767     }
768 }
769
770 impl Encodable for SyntaxContext {
771     fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
772         Ok(()) // FIXME(jseyfried) intercrate hygiene
773     }
774 }
775
776 impl Decodable for SyntaxContext {
777     fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
778         Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
779     }
780 }