]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_pos/hygiene.rs
Rollup merge of #58239 - taiki-e:librustc_apfloat-2018, r=Centril
[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 use crate::GLOBALS;
9 use crate::Span;
10 use crate::edition::{Edition, DEFAULT_EDITION};
11 use crate::symbol::{keywords, Symbol};
12
13 use serialize::{Encodable, Decodable, Encoder, Decoder};
14 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
15 use std::{fmt, mem};
16
17 /// A SyntaxContext represents a chain of macro expansions (represented by marks).
18 #[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
19 pub struct SyntaxContext(u32);
20
21 #[derive(Copy, Clone, Debug)]
22 struct SyntaxContextData {
23     outer_mark: Mark,
24     transparency: Transparency,
25     prev_ctxt: SyntaxContext,
26     // This context, but with all transparent and semi-transparent marks filtered away.
27     opaque: SyntaxContext,
28     // This context, but with all transparent marks filtered away.
29     opaque_and_semitransparent: SyntaxContext,
30     // Name of the crate to which `$crate` with this context would resolve.
31     dollar_crate_name: Symbol,
32 }
33
34 /// A mark is a unique id associated with a macro expansion.
35 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
36 pub struct Mark(u32);
37
38 #[derive(Clone, Debug)]
39 struct MarkData {
40     parent: Mark,
41     default_transparency: Transparency,
42     expn_info: Option<ExpnInfo>,
43 }
44
45 /// A property of a macro expansion that determines how identifiers
46 /// produced by that expansion are resolved.
47 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
48 pub enum Transparency {
49     /// Identifier produced by a transparent expansion is always resolved at call-site.
50     /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
51     Transparent,
52     /// Identifier produced by a semi-transparent expansion may be resolved
53     /// either at call-site or at definition-site.
54     /// If it's a local variable, label or `$crate` then it's resolved at def-site.
55     /// Otherwise it's resolved at call-site.
56     /// `macro_rules` macros behave like this, built-in macros currently behave like this too,
57     /// but that's an implementation detail.
58     SemiTransparent,
59     /// Identifier produced by an opaque expansion is always resolved at definition-site.
60     /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
61     Opaque,
62 }
63
64 impl Mark {
65     pub fn fresh(parent: Mark) -> Self {
66         HygieneData::with(|data| {
67             data.marks.push(MarkData {
68                 parent,
69                 // By default expansions behave like `macro_rules`.
70                 default_transparency: Transparency::SemiTransparent,
71                 expn_info: None,
72             });
73             Mark(data.marks.len() as u32 - 1)
74         })
75     }
76
77     /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
78     #[inline]
79     pub fn root() -> Self {
80         Mark(0)
81     }
82
83     #[inline]
84     pub fn as_u32(self) -> u32 {
85         self.0
86     }
87
88     #[inline]
89     pub fn from_u32(raw: u32) -> Mark {
90         Mark(raw)
91     }
92
93     #[inline]
94     pub fn parent(self) -> Mark {
95         HygieneData::with(|data| data.marks[self.0 as usize].parent)
96     }
97
98     #[inline]
99     pub fn expn_info(self) -> Option<ExpnInfo> {
100         HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone())
101     }
102
103     #[inline]
104     pub fn set_expn_info(self, info: ExpnInfo) {
105         HygieneData::with(|data| data.marks[self.0 as usize].expn_info = Some(info))
106     }
107
108     #[inline]
109     pub fn set_default_transparency(self, transparency: Transparency) {
110         assert_ne!(self, Mark::root());
111         HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency)
112     }
113
114     pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
115         HygieneData::with(|data| {
116             while self != ancestor {
117                 if self == Mark::root() {
118                     return false;
119                 }
120                 self = data.marks[self.0 as usize].parent;
121             }
122             true
123         })
124     }
125
126     /// Computes a mark such that both input marks are descendants of (or equal to) the returned
127     /// mark. That is, the following holds:
128     ///
129     /// ```rust
130     /// let la = least_ancestor(a, b);
131     /// assert!(a.is_descendant_of(la))
132     /// assert!(b.is_descendant_of(la))
133     /// ```
134     pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
135         HygieneData::with(|data| {
136             // Compute the path from a to the root
137             let mut a_path = FxHashSet::<Mark>::default();
138             while a != Mark::root() {
139                 a_path.insert(a);
140                 a = data.marks[a.0 as usize].parent;
141             }
142
143             // While the path from b to the root hasn't intersected, move up the tree
144             while !a_path.contains(&b) {
145                 b = data.marks[b.0 as usize].parent;
146             }
147
148             b
149         })
150     }
151
152     // Used for enabling some compatibility fallback in resolve.
153     #[inline]
154     pub fn looks_like_proc_macro_derive(self) -> bool {
155         HygieneData::with(|data| {
156             let mark_data = &data.marks[self.0 as usize];
157             if mark_data.default_transparency == Transparency::Opaque {
158                 if let Some(expn_info) = &mark_data.expn_info {
159                     if let ExpnFormat::MacroAttribute(name) = expn_info.format {
160                         if name.as_str().starts_with("derive(") {
161                             return true;
162                         }
163                     }
164                 }
165             }
166             false
167         })
168     }
169 }
170
171 #[derive(Debug)]
172 crate struct HygieneData {
173     marks: Vec<MarkData>,
174     syntax_contexts: Vec<SyntaxContextData>,
175     markings: FxHashMap<(SyntaxContext, Mark, Transparency), SyntaxContext>,
176     default_edition: Edition,
177 }
178
179 impl HygieneData {
180     crate fn new() -> Self {
181         HygieneData {
182             marks: vec![MarkData {
183                 parent: Mark::root(),
184                 // If the root is opaque, then loops searching for an opaque mark
185                 // will automatically stop after reaching it.
186                 default_transparency: Transparency::Opaque,
187                 expn_info: None,
188             }],
189             syntax_contexts: vec![SyntaxContextData {
190                 outer_mark: Mark::root(),
191                 transparency: Transparency::Opaque,
192                 prev_ctxt: SyntaxContext(0),
193                 opaque: SyntaxContext(0),
194                 opaque_and_semitransparent: SyntaxContext(0),
195                 dollar_crate_name: keywords::DollarCrate.name(),
196             }],
197             markings: FxHashMap::default(),
198             default_edition: DEFAULT_EDITION,
199         }
200     }
201
202     fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
203         GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut()))
204     }
205 }
206
207 pub fn default_edition() -> Edition {
208     HygieneData::with(|data| data.default_edition)
209 }
210
211 pub fn set_default_edition(edition: Edition) {
212     HygieneData::with(|data| data.default_edition = edition);
213 }
214
215 pub fn clear_markings() {
216     HygieneData::with(|data| data.markings = FxHashMap::default());
217 }
218
219 impl SyntaxContext {
220     pub const fn empty() -> Self {
221         SyntaxContext(0)
222     }
223
224     crate fn as_u32(self) -> u32 {
225         self.0
226     }
227
228     crate fn from_u32(raw: u32) -> SyntaxContext {
229         SyntaxContext(raw)
230     }
231
232     // Allocate a new SyntaxContext with the given ExpnInfo. This is used when
233     // deserializing Spans from the incr. comp. cache.
234     // FIXME(mw): This method does not restore MarkData::parent or
235     // SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
236     // don't seem to be used after HIR lowering, so everything should be fine
237     // as long as incremental compilation does not kick in before that.
238     pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
239         HygieneData::with(|data| {
240             data.marks.push(MarkData {
241                 parent: Mark::root(),
242                 default_transparency: Transparency::SemiTransparent,
243                 expn_info: Some(expansion_info),
244             });
245
246             let mark = Mark(data.marks.len() as u32 - 1);
247
248             data.syntax_contexts.push(SyntaxContextData {
249                 outer_mark: mark,
250                 transparency: Transparency::SemiTransparent,
251                 prev_ctxt: SyntaxContext::empty(),
252                 opaque: SyntaxContext::empty(),
253                 opaque_and_semitransparent: SyntaxContext::empty(),
254                 dollar_crate_name: keywords::DollarCrate.name(),
255             });
256             SyntaxContext(data.syntax_contexts.len() as u32 - 1)
257         })
258     }
259
260     /// Extend a syntax context with a given mark and default transparency for that mark.
261     pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
262         assert_ne!(mark, Mark::root());
263         self.apply_mark_with_transparency(
264             mark, HygieneData::with(|data| data.marks[mark.0 as usize].default_transparency)
265         )
266     }
267
268     /// Extend a syntax context with a given mark and transparency
269     pub fn apply_mark_with_transparency(self, mark: Mark, transparency: Transparency)
270                                         -> SyntaxContext {
271         assert_ne!(mark, Mark::root());
272         if transparency == Transparency::Opaque {
273             return self.apply_mark_internal(mark, transparency);
274         }
275
276         let call_site_ctxt =
277             mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
278         let call_site_ctxt = if transparency == Transparency::SemiTransparent {
279             call_site_ctxt.modern()
280         } else {
281             call_site_ctxt.modern_and_legacy()
282         };
283
284         if call_site_ctxt == SyntaxContext::empty() {
285             return self.apply_mark_internal(mark, transparency);
286         }
287
288         // Otherwise, `mark` is a macros 1.0 definition and the call site is in a
289         // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition.
290         //
291         // In this case, the tokens from the macros 1.0 definition inherit the hygiene
292         // at their invocation. That is, we pretend that the macros 1.0 definition
293         // was defined at its invocation (i.e., inside the macros 2.0 definition)
294         // so that the macros 2.0 definition remains hygienic.
295         //
296         // See the example at `test/run-pass/hygiene/legacy_interaction.rs`.
297         let mut ctxt = call_site_ctxt;
298         for (mark, transparency) in self.marks() {
299             ctxt = ctxt.apply_mark_internal(mark, transparency);
300         }
301         ctxt.apply_mark_internal(mark, transparency)
302     }
303
304     fn apply_mark_internal(self, mark: Mark, transparency: Transparency) -> SyntaxContext {
305         HygieneData::with(|data| {
306             let syntax_contexts = &mut data.syntax_contexts;
307             let mut opaque = syntax_contexts[self.0 as usize].opaque;
308             let mut opaque_and_semitransparent =
309                 syntax_contexts[self.0 as usize].opaque_and_semitransparent;
310
311             if transparency >= Transparency::Opaque {
312                 let prev_ctxt = opaque;
313                 opaque = *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
314                     let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
315                     syntax_contexts.push(SyntaxContextData {
316                         outer_mark: mark,
317                         transparency,
318                         prev_ctxt,
319                         opaque: new_opaque,
320                         opaque_and_semitransparent: new_opaque,
321                         dollar_crate_name: keywords::DollarCrate.name(),
322                     });
323                     new_opaque
324                 });
325             }
326
327             if transparency >= Transparency::SemiTransparent {
328                 let prev_ctxt = opaque_and_semitransparent;
329                 opaque_and_semitransparent =
330                         *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
331                     let new_opaque_and_semitransparent =
332                         SyntaxContext(syntax_contexts.len() as u32);
333                     syntax_contexts.push(SyntaxContextData {
334                         outer_mark: mark,
335                         transparency,
336                         prev_ctxt,
337                         opaque,
338                         opaque_and_semitransparent: new_opaque_and_semitransparent,
339                         dollar_crate_name: keywords::DollarCrate.name(),
340                     });
341                     new_opaque_and_semitransparent
342                 });
343             }
344
345             let prev_ctxt = self;
346             *data.markings.entry((prev_ctxt, mark, transparency)).or_insert_with(|| {
347                 let new_opaque_and_semitransparent_and_transparent =
348                     SyntaxContext(syntax_contexts.len() as u32);
349                 syntax_contexts.push(SyntaxContextData {
350                     outer_mark: mark,
351                     transparency,
352                     prev_ctxt,
353                     opaque,
354                     opaque_and_semitransparent,
355                     dollar_crate_name: keywords::DollarCrate.name(),
356                 });
357                 new_opaque_and_semitransparent_and_transparent
358             })
359         })
360     }
361
362     /// Pulls a single mark off of the syntax context. This effectively moves the
363     /// context up one macro definition level. That is, if we have a nested macro
364     /// definition as follows:
365     ///
366     /// ```rust
367     /// macro_rules! f {
368     ///    macro_rules! g {
369     ///        ...
370     ///    }
371     /// }
372     /// ```
373     ///
374     /// and we have a SyntaxContext that is referring to something declared by an invocation
375     /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the
376     /// invocation of f that created g1.
377     /// Returns the mark that was removed.
378     pub fn remove_mark(&mut self) -> Mark {
379         HygieneData::with(|data| {
380             let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
381             *self = data.syntax_contexts[self.0 as usize].prev_ctxt;
382             outer_mark
383         })
384     }
385
386     pub fn marks(mut self) -> Vec<(Mark, Transparency)> {
387         HygieneData::with(|data| {
388             let mut marks = Vec::new();
389             while self != SyntaxContext::empty() {
390                 let ctxt_data = &data.syntax_contexts[self.0 as usize];
391                 marks.push((ctxt_data.outer_mark, ctxt_data.transparency));
392                 self = ctxt_data.prev_ctxt;
393             }
394             marks.reverse();
395             marks
396         })
397     }
398
399     /// Adjust this context for resolution in a scope created by the given expansion.
400     /// For example, consider the following three resolutions of `f`:
401     ///
402     /// ```rust
403     /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty.
404     /// m!(f);
405     /// macro m($f:ident) {
406     ///     mod bar {
407     ///         pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
408     ///         pub fn $f() {} // `$f`'s `SyntaxContext` is empty.
409     ///     }
410     ///     foo::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
411     ///     //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`,
412     ///     //| and it resolves to `::foo::f`.
413     ///     bar::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
414     ///     //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`,
415     ///     //| and it resolves to `::bar::f`.
416     ///     bar::$f(); // `f`'s `SyntaxContext` is empty.
417     ///     //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`,
418     ///     //| and it resolves to `::bar::$f`.
419     /// }
420     /// ```
421     /// This returns the expansion whose definition scope we use to privacy check the resolution,
422     /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope).
423     pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
424         let mut scope = None;
425         while !expansion.is_descendant_of(self.outer()) {
426             scope = Some(self.remove_mark());
427         }
428         scope
429     }
430
431     /// Adjust this context for resolution in a scope created by the given expansion
432     /// via a glob import with the given `SyntaxContext`.
433     /// For example:
434     ///
435     /// ```rust
436     /// m!(f);
437     /// macro m($i:ident) {
438     ///     mod foo {
439     ///         pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
440     ///         pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
441     ///     }
442     ///     n(f);
443     ///     macro n($j:ident) {
444     ///         use foo::*;
445     ///         f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
446     ///         //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`.
447     ///         $i(); // `$i`'s `SyntaxContext` has a mark from `n`
448     ///         //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`.
449     ///         $j(); // `$j`'s `SyntaxContext` has a mark from `m`
450     ///         //^ This cannot be glob-adjusted, so this is a resolution error.
451     ///     }
452     /// }
453     /// ```
454     /// This returns `None` if the context cannot be glob-adjusted.
455     /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details).
456     pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
457                        -> Option<Option<Mark>> {
458         let mut scope = None;
459         while !expansion.is_descendant_of(glob_ctxt.outer()) {
460             scope = Some(glob_ctxt.remove_mark());
461             if self.remove_mark() != scope.unwrap() {
462                 return None;
463             }
464         }
465         if self.adjust(expansion).is_some() {
466             return None;
467         }
468         Some(scope)
469     }
470
471     /// Undo `glob_adjust` if possible:
472     ///
473     /// ```rust
474     /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) {
475     ///     assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope));
476     /// }
477     /// ```
478     pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
479                                -> Option<Option<Mark>> {
480         if self.adjust(expansion).is_some() {
481             return None;
482         }
483
484         let mut marks = Vec::new();
485         while !expansion.is_descendant_of(glob_ctxt.outer()) {
486             marks.push(glob_ctxt.remove_mark());
487         }
488
489         let scope = marks.last().cloned();
490         while let Some(mark) = marks.pop() {
491             *self = self.apply_mark(mark);
492         }
493         Some(scope)
494     }
495
496     #[inline]
497     pub fn modern(self) -> SyntaxContext {
498         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
499     }
500
501     #[inline]
502     pub fn modern_and_legacy(self) -> SyntaxContext {
503         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
504     }
505
506     #[inline]
507     pub fn outer(self) -> Mark {
508         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
509     }
510
511     pub fn dollar_crate_name(self) -> Symbol {
512         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
513     }
514
515     pub fn set_dollar_crate_name(self, dollar_crate_name: Symbol) {
516         HygieneData::with(|data| {
517             let prev_dollar_crate_name = mem::replace(
518                 &mut data.syntax_contexts[self.0 as usize].dollar_crate_name, dollar_crate_name
519             );
520             assert!(dollar_crate_name == prev_dollar_crate_name ||
521                     prev_dollar_crate_name == keywords::DollarCrate.name(),
522                     "$crate name is reset for a syntax context");
523         })
524     }
525 }
526
527 impl fmt::Debug for SyntaxContext {
528     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529         write!(f, "#{}", self.0)
530     }
531 }
532
533 /// Extra information for tracking spans of macro and syntax sugar expansion
534 #[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
535 pub struct ExpnInfo {
536     /// The location of the actual macro invocation or syntax sugar , e.g.
537     /// `let x = foo!();` or `if let Some(y) = x {}`
538     ///
539     /// This may recursively refer to other macro invocations, e.g., if
540     /// `foo!()` invoked `bar!()` internally, and there was an
541     /// expression inside `bar!`; the call_site of the expression in
542     /// the expansion would point to the `bar!` invocation; that
543     /// call_site span would have its own ExpnInfo, with the call_site
544     /// pointing to the `foo!` invocation.
545     pub call_site: Span,
546     /// The span of the macro definition itself. The macro may not
547     /// have a sensible definition span (e.g., something defined
548     /// completely inside libsyntax) in which case this is None.
549     /// This span serves only informational purpose and is not used for resolution.
550     pub def_site: Option<Span>,
551     /// The format with which the macro was invoked.
552     pub format: ExpnFormat,
553     /// Whether the macro is allowed to use #[unstable]/feature-gated
554     /// features internally without forcing the whole crate to opt-in
555     /// to them.
556     pub allow_internal_unstable: bool,
557     /// Whether the macro is allowed to use `unsafe` internally
558     /// even if the user crate has `#![forbid(unsafe_code)]`.
559     pub allow_internal_unsafe: bool,
560     /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`)
561     /// for a given macro.
562     pub local_inner_macros: bool,
563     /// Edition of the crate in which the macro is defined.
564     pub edition: Edition,
565 }
566
567 /// The source of expansion.
568 #[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
569 pub enum ExpnFormat {
570     /// e.g., #[derive(...)] <item>
571     MacroAttribute(Symbol),
572     /// e.g., `format!()`
573     MacroBang(Symbol),
574     /// Desugaring done by the compiler during HIR lowering.
575     CompilerDesugaring(CompilerDesugaringKind)
576 }
577
578 impl ExpnFormat {
579     pub fn name(&self) -> Symbol {
580         match *self {
581             ExpnFormat::MacroBang(name) | ExpnFormat::MacroAttribute(name) => name,
582             ExpnFormat::CompilerDesugaring(kind) => kind.name(),
583         }
584     }
585 }
586
587 /// The kind of compiler desugaring.
588 #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
589 pub enum CompilerDesugaringKind {
590     QuestionMark,
591     TryBlock,
592     /// Desugaring of an `impl Trait` in return type position
593     /// to an `existential type Foo: Trait;` + replacing the
594     /// `impl Trait` with `Foo`.
595     ExistentialReturnType,
596     Async,
597     ForLoop,
598 }
599
600 impl CompilerDesugaringKind {
601     pub fn name(self) -> Symbol {
602         Symbol::intern(match self {
603             CompilerDesugaringKind::Async => "async",
604             CompilerDesugaringKind::QuestionMark => "?",
605             CompilerDesugaringKind::TryBlock => "try block",
606             CompilerDesugaringKind::ExistentialReturnType => "existential type",
607             CompilerDesugaringKind::ForLoop => "for loop",
608         })
609     }
610 }
611
612 impl Encodable for SyntaxContext {
613     fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
614         Ok(()) // FIXME(jseyfried) intercrate hygiene
615     }
616 }
617
618 impl Decodable for SyntaxContext {
619     fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
620         Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
621     }
622 }