]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_pos/hygiene.rs
8a9ff647b3ea1b9a4504aa55f3ccd4f0766a212c
[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.
14 //! 2012. *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 http://dx.doi.org/10.1017/S0956796812000093
17
18 use Span;
19 use symbol::Symbol;
20
21 use serialize::{Encodable, Decodable, Encoder, Decoder};
22 use std::cell::RefCell;
23 use std::collections::HashMap;
24 use std::fmt;
25
26 /// A SyntaxContext represents a chain of macro expansions (represented by marks).
27 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28 pub struct SyntaxContext(u32);
29
30 #[derive(Copy, Clone)]
31 pub struct SyntaxContextData {
32     pub outer_mark: Mark,
33     pub prev_ctxt: SyntaxContext,
34 }
35
36 /// A mark is a unique id associated with a macro expansion.
37 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)]
38 pub struct Mark(u32);
39
40 impl Mark {
41     pub fn fresh() -> Self {
42         HygieneData::with(|data| {
43             data.marks.push(None);
44             Mark(data.marks.len() as u32 - 1)
45         })
46     }
47
48     /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
49     pub fn root() -> Self {
50         Mark(0)
51     }
52
53     pub fn as_u32(self) -> u32 {
54         self.0
55     }
56
57     pub fn from_u32(raw: u32) -> Mark {
58         Mark(raw)
59     }
60
61     pub fn expn_info(self) -> Option<ExpnInfo> {
62         HygieneData::with(|data| data.marks[self.0 as usize].clone())
63     }
64
65     pub fn set_expn_info(self, info: ExpnInfo) {
66         HygieneData::with(|data| data.marks[self.0 as usize] = Some(info))
67     }
68 }
69
70 struct HygieneData {
71     marks: Vec<Option<ExpnInfo>>,
72     syntax_contexts: Vec<SyntaxContextData>,
73     markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
74 }
75
76 impl HygieneData {
77     fn new() -> Self {
78         HygieneData {
79             marks: vec![None],
80             syntax_contexts: vec![SyntaxContextData {
81                 outer_mark: Mark::root(),
82                 prev_ctxt: SyntaxContext::empty(),
83             }],
84             markings: HashMap::new(),
85         }
86     }
87
88     fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
89         thread_local! {
90             static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
91         }
92         HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
93     }
94 }
95
96 pub fn clear_markings() {
97     HygieneData::with(|data| data.markings = HashMap::new());
98 }
99
100 impl SyntaxContext {
101     pub const fn empty() -> Self {
102         SyntaxContext(0)
103     }
104
105     pub fn data(self) -> SyntaxContextData {
106         HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
107     }
108
109     /// Extend a syntax context with a given mark
110     pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
111         // Applying the same mark twice is a no-op
112         let ctxt_data = self.data();
113         if mark == ctxt_data.outer_mark {
114             return ctxt_data.prev_ctxt;
115         }
116
117         HygieneData::with(|data| {
118             let syntax_contexts = &mut data.syntax_contexts;
119             *data.markings.entry((self, mark)).or_insert_with(|| {
120                 syntax_contexts.push(SyntaxContextData {
121                     outer_mark: mark,
122                     prev_ctxt: self,
123                 });
124                 SyntaxContext(syntax_contexts.len() as u32 - 1)
125             })
126         })
127     }
128
129     pub fn outer(self) -> Mark {
130         HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
131     }
132 }
133
134 impl fmt::Debug for SyntaxContext {
135     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136         write!(f, "#{}", self.0)
137     }
138 }
139
140 /// Extra information for tracking spans of macro and syntax sugar expansion
141 #[derive(Clone, Hash, Debug)]
142 pub struct ExpnInfo {
143     /// The location of the actual macro invocation or syntax sugar , e.g.
144     /// `let x = foo!();` or `if let Some(y) = x {}`
145     ///
146     /// This may recursively refer to other macro invocations, e.g. if
147     /// `foo!()` invoked `bar!()` internally, and there was an
148     /// expression inside `bar!`; the call_site of the expression in
149     /// the expansion would point to the `bar!` invocation; that
150     /// call_site span would have its own ExpnInfo, with the call_site
151     /// pointing to the `foo!` invocation.
152     pub call_site: Span,
153     /// Information about the expansion.
154     pub callee: NameAndSpan
155 }
156
157 #[derive(Clone, Hash, Debug)]
158 pub struct NameAndSpan {
159     /// The format with which the macro was invoked.
160     pub format: ExpnFormat,
161     /// Whether the macro is allowed to use #[unstable]/feature-gated
162     /// features internally without forcing the whole crate to opt-in
163     /// to them.
164     pub allow_internal_unstable: bool,
165     /// The span of the macro definition itself. The macro may not
166     /// have a sensible definition span (e.g. something defined
167     /// completely inside libsyntax) in which case this is None.
168     pub span: Option<Span>
169 }
170
171 impl NameAndSpan {
172     pub fn name(&self) -> Symbol {
173         match self.format {
174             ExpnFormat::MacroAttribute(s) |
175             ExpnFormat::MacroBang(s) |
176             ExpnFormat::CompilerDesugaring(s) => s,
177         }
178     }
179 }
180
181 /// The source of expansion.
182 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
183 pub enum ExpnFormat {
184     /// e.g. #[derive(...)] <item>
185     MacroAttribute(Symbol),
186     /// e.g. `format!()`
187     MacroBang(Symbol),
188     /// Desugaring done by the compiler during HIR lowering.
189     CompilerDesugaring(Symbol)
190 }
191
192 impl Encodable for SyntaxContext {
193     fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
194         Ok(()) // FIXME(jseyfried) intercrate hygiene
195     }
196 }
197
198 impl Decodable for SyntaxContext {
199     fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
200         Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene
201     }
202 }