use GLOBALS;
use Span;
use edition::Edition;
use symbol::{Ident, Symbol};
use serialize::{Encodable, Decodable, Encoder, Decoder};
use std::collections::HashMap;
use rustc_data_structures::fx::FxHashSet;
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
pub struct SyntaxContext(pub(super) u32);
#[derive(Copy, Clone)]
pub struct SyntaxContextData {
pub outer_mark: Mark,
pub prev_ctxt: SyntaxContext,
pub modern: SyntaxContext,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct Mark(u32);
struct MarkData {
parent: Mark,
kind: MarkKind,
expn_info: Option<ExpnInfo>,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum MarkKind {
Modern,
Builtin,
Legacy,
}
impl Mark {
pub fn fresh(parent: Mark) -> Self {
HygieneData::with(|data| {
data.marks.push(MarkData { parent: parent, kind: MarkKind::Legacy, expn_info: None });
Mark(data.marks.len() as u32 - 1)
})
}
#[inline]
pub fn root() -> Self {
Mark(0)
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_u32(raw: u32) -> Mark {
Mark(raw)
}
#[inline]
pub fn expn_info(self) -> Option<ExpnInfo> {
HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone())
}
#[inline]
pub fn set_expn_info(self, info: ExpnInfo) {
HygieneData::with(|data| data.marks[self.0 as usize].expn_info = Some(info))
}
pub fn modern(mut self) -> Mark {
HygieneData::with(|data| {
loop {
if self == Mark::root() || data.marks[self.0 as usize].kind == MarkKind::Modern {
return self;
}
self = data.marks[self.0 as usize].parent;
}
})
}
#[inline]
pub fn kind(self) -> MarkKind {
HygieneData::with(|data| data.marks[self.0 as usize].kind)
}
#[inline]
pub fn set_kind(self, kind: MarkKind) {
HygieneData::with(|data| data.marks[self.0 as usize].kind = kind)
}
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
HygieneData::with(|data| {
while self != ancestor {
if self == Mark::root() {
return false;
}
self = data.marks[self.0 as usize].parent;
}
true
})
}
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
HygieneData::with(|data| {
let mut a_path = FxHashSet::<Mark>();
while a != Mark::root() {
a_path.insert(a);
a = data.marks[a.0 as usize].parent;
}
while !a_path.contains(&b) {
b = data.marks[b.0 as usize].parent;
}
b
})
}
}
pub struct HygieneData {
marks: Vec<MarkData>,
syntax_contexts: Vec<SyntaxContextData>,
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
gensym_to_ctxt: HashMap<Symbol, Span>,
default_edition: Edition,
}
impl HygieneData {
pub fn new() -> Self {
HygieneData {
marks: vec![MarkData {
parent: Mark::root(),
kind: MarkKind::Builtin,
expn_info: None,
}],
syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark::root(),
prev_ctxt: SyntaxContext(0),
modern: SyntaxContext(0),
}],
markings: HashMap::new(),
gensym_to_ctxt: HashMap::new(),
default_edition: Edition::Edition2015,
}
}
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut()))
}
}
pub fn default_edition() -> Edition {
HygieneData::with(|data| data.default_edition)
}
pub fn set_default_edition(edition: Edition) {
HygieneData::with(|data| data.default_edition = edition);
}
pub fn clear_markings() {
HygieneData::with(|data| data.markings = HashMap::new());
}
impl SyntaxContext {
pub const fn empty() -> Self {
SyntaxContext(0)
}
pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
HygieneData::with(|data| {
data.marks.push(MarkData {
parent: Mark::root(),
kind: MarkKind::Legacy,
expn_info: Some(expansion_info)
});
let mark = Mark(data.marks.len() as u32 - 1);
data.syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt: SyntaxContext::empty(),
modern: SyntaxContext::empty(),
});
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
})
}
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
if mark.kind() == MarkKind::Modern {
return self.apply_mark_internal(mark);
}
let call_site_ctxt =
mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
if call_site_ctxt == SyntaxContext::empty() {
return self.apply_mark_internal(mark);
}
let mut ctxt = call_site_ctxt;
for mark in self.marks() {
ctxt = ctxt.apply_mark_internal(mark);
}
ctxt.apply_mark_internal(mark)
}
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
HygieneData::with(|data| {
let syntax_contexts = &mut data.syntax_contexts;
let mut modern = syntax_contexts[self.0 as usize].modern;
if data.marks[mark.0 as usize].kind == MarkKind::Modern {
modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
let len = syntax_contexts.len() as u32;
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt: modern,
modern: SyntaxContext(len),
});
SyntaxContext(len)
});
}
*data.markings.entry((self, mark)).or_insert_with(|| {
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt: self,
modern,
});
SyntaxContext(syntax_contexts.len() as u32 - 1)
})
})
}
pub fn remove_mark(&mut self) -> Mark {
HygieneData::with(|data| {
let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
*self = data.syntax_contexts[self.0 as usize].prev_ctxt;
outer_mark
})
}
pub fn marks(mut self) -> Vec<Mark> {
HygieneData::with(|data| {
let mut marks = Vec::new();
while self != SyntaxContext::empty() {
marks.push(data.syntax_contexts[self.0 as usize].outer_mark);
self = data.syntax_contexts[self.0 as usize].prev_ctxt;
}
marks.reverse();
marks
})
}
pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
let mut scope = None;
while !expansion.is_descendant_of(self.outer()) {
scope = Some(self.remove_mark());
}
scope
}
pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
-> Option<Option<Mark>> {
let mut scope = None;
while !expansion.is_descendant_of(glob_ctxt.outer()) {
scope = Some(glob_ctxt.remove_mark());
if self.remove_mark() != scope.unwrap() {
return None;
}
}
if self.adjust(expansion).is_some() {
return None;
}
Some(scope)
}
pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext)
-> Option<Option<Mark>> {
if self.adjust(expansion).is_some() {
return None;
}
let mut marks = Vec::new();
while !expansion.is_descendant_of(glob_ctxt.outer()) {
marks.push(glob_ctxt.remove_mark());
}
let scope = marks.last().cloned();
while let Some(mark) = marks.pop() {
*self = self.apply_mark(mark);
}
Some(scope)
}
#[inline]
pub fn modern(self) -> SyntaxContext {
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
}
#[inline]
pub fn outer(self) -> Mark {
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
}
}
impl fmt::Debug for SyntaxContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct ExpnInfo {
pub call_site: Span,
pub callee: NameAndSpan
}
#[derive(Clone, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct NameAndSpan {
pub format: ExpnFormat,
pub allow_internal_unstable: bool,
pub allow_internal_unsafe: bool,
pub edition: Edition,
pub span: Option<Span>
}
impl NameAndSpan {
pub fn name(&self) -> Symbol {
match self.format {
ExpnFormat::MacroAttribute(s) |
ExpnFormat::MacroBang(s) => s,
ExpnFormat::CompilerDesugaring(ref kind) => kind.as_symbol(),
}
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum ExpnFormat {
MacroAttribute(Symbol),
MacroBang(Symbol),
CompilerDesugaring(CompilerDesugaringKind)
}
#[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum CompilerDesugaringKind {
DotFill,
QuestionMark,
Catch,
}
impl CompilerDesugaringKind {
pub fn as_symbol(&self) -> Symbol {
use CompilerDesugaringKind::*;
let s = match *self {
DotFill => "...",
QuestionMark => "?",
Catch => "do catch",
};
Symbol::intern(s)
}
}
impl Encodable for SyntaxContext {
fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> {
Ok(()) }
}
impl Decodable for SyntaxContext {
fn decode<D: Decoder>(_: &mut D) -> Result<SyntaxContext, D::Error> {
Ok(SyntaxContext::empty()) }
}
impl Symbol {
pub fn from_ident(ident: Ident) -> Symbol {
HygieneData::with(|data| {
let gensym = ident.name.gensymed();
data.gensym_to_ctxt.insert(gensym, ident.span);
gensym
})
}
pub fn to_ident(self) -> Ident {
HygieneData::with(|data| {
match data.gensym_to_ctxt.get(&self) {
Some(&span) => Ident::new(self.interned(), span),
None => Ident::with_empty_ctxt(self),
}
})
}
}