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.
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.
11 //! Machinery for hygienic macros, inspired by the MTWT[1] paper.
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
21 use serialize::{Encodable, Decodable, Encoder, Decoder};
22 use std::cell::RefCell;
23 use std::collections::HashMap;
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);
30 #[derive(Copy, Clone)]
31 pub struct SyntaxContextData {
33 pub prev_ctxt: SyntaxContext,
36 /// A mark is a unique id associated with a macro expansion.
37 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)]
41 pub fn fresh() -> Self {
42 HygieneData::with(|data| {
43 data.marks.push(None);
44 Mark(data.marks.len() as u32 - 1)
48 /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
49 pub fn root() -> Self {
53 pub fn as_u32(self) -> u32 {
57 pub fn from_u32(raw: u32) -> Mark {
61 pub fn expn_info(self) -> Option<ExpnInfo> {
62 HygieneData::with(|data| data.marks[self.0 as usize].clone())
65 pub fn set_expn_info(self, info: ExpnInfo) {
66 HygieneData::with(|data| data.marks[self.0 as usize] = Some(info))
71 marks: Vec<Option<ExpnInfo>>,
72 syntax_contexts: Vec<SyntaxContextData>,
73 markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
80 syntax_contexts: vec![SyntaxContextData {
81 outer_mark: Mark::root(),
82 prev_ctxt: SyntaxContext::empty(),
84 markings: HashMap::new(),
88 fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
90 static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
92 HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
96 pub fn clear_markings() {
97 HygieneData::with(|data| data.markings = HashMap::new());
101 pub const fn empty() -> Self {
105 pub fn data(self) -> SyntaxContextData {
106 HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
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;
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 {
124 SyntaxContext(syntax_contexts.len() as u32 - 1)
129 pub fn outer(self) -> Mark {
130 HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
134 impl fmt::Debug for SyntaxContext {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 write!(f, "#{}", self.0)
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 {}`
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.
153 /// Information about the expansion.
154 pub callee: NameAndSpan
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
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>
172 pub fn name(&self) -> Symbol {
174 ExpnFormat::MacroAttribute(s) |
175 ExpnFormat::MacroBang(s) |
176 ExpnFormat::CompilerDesugaring(s) => s,
181 /// The source of expansion.
182 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
183 pub enum ExpnFormat {
184 /// e.g. #[derive(...)] <item>
185 MacroAttribute(Symbol),
188 /// Desugaring done by the compiler during HIR lowering.
189 CompilerDesugaring(Symbol)
192 impl Encodable for SyntaxContext {
193 fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
194 Ok(()) // FIXME(jseyfried) intercrate hygiene
198 impl Decodable for SyntaxContext {
199 fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
200 Ok(SyntaxContext::empty()) // FIXME(jseyfried) intercrate hygiene