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