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