use rustc::session::Session;
use std::collections::HashMap;
use std::mem;
use std::ops::{Deref, DerefMut};
use syntax::ast::*;
use syntax::source_map::{Span, DUMMY_SP};
use syntax::util::parser;
use crate::ast_manip::{map_ast, AstMap};
use crate::ast_manip::{GetSpan, Visit, CommentMap};
use crate::driver;
mod cleanup;
pub mod files;
pub mod json;
mod base;
mod strategy;
pub use self::base::Rewrite;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum TextAdjust {
None,
Parenthesize,
}
#[derive(Clone, PartialEq, Debug)]
pub struct TextRewrite {
pub old_span: Span,
pub new_span: Span,
pub rewrites: Vec<TextRewrite>,
pub nodes: Vec<(Span, NodeId)>,
pub adjust: TextAdjust,
}
impl TextRewrite {
pub fn new(old_span: Span, new_span: Span) -> TextRewrite {
Self::adjusted(old_span, new_span, TextAdjust::None)
}
pub fn adjusted(old_span: Span, new_span: Span, adjust: TextAdjust) -> TextRewrite {
TextRewrite {
old_span,
new_span,
adjust,
rewrites: Vec::new(),
nodes: Vec::new(),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SeqItemId {
Node(NodeId),
Attr(AttrId),
}
trait MappableId {
fn map_id(self, rcx: &RewriteCtxt) -> Self;
}
impl MappableId for NodeId {
fn map_id(self, rcx: &RewriteCtxt) -> Self {
rcx.node_id_map.get(&self).map_or(DUMMY_NODE_ID, |&x| x)
}
}
impl MappableId for AttrId {
fn map_id(self, _rcx: &RewriteCtxt) -> Self {
self
}
}
impl MappableId for SeqItemId {
fn map_id(self, rcx: &RewriteCtxt) -> Self {
match self {
SeqItemId::Node(id) => SeqItemId::Node(id.map_id(rcx)),
SeqItemId::Attr(id) => SeqItemId::Attr(id.map_id(rcx)),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ExprPrec {
Normal(i8),
Cond(i8),
Callee(i8),
LeftLess(i8),
}
pub struct RewriteCtxt<'s> {
sess: &'s Session,
old_nodes: AstMap<'s>,
comment_map: &'s CommentMap,
text_span_cache: HashMap<String, Span>,
fresh_start: Span,
expr_prec: ExprPrec,
node_id_map: HashMap<NodeId, NodeId>,
}
impl<'s> RewriteCtxt<'s> {
fn new(
sess: &'s Session,
old_nodes: AstMap<'s>,
comment_map: &'s CommentMap,
node_id_map: HashMap<NodeId, NodeId>,
) -> RewriteCtxt<'s> {
RewriteCtxt {
sess,
old_nodes,
comment_map,
text_span_cache: HashMap::new(),
fresh_start: DUMMY_SP,
expr_prec: ExprPrec::Normal(parser::PREC_RESET),
node_id_map,
}
}
pub fn session(&self) -> &'s Session {
self.sess
}
pub fn old_nodes(&self) -> &AstMap<'s> {
&self.old_nodes
}
pub fn comments(&self) -> &'s CommentMap {
&self.comment_map
}
pub fn fresh_start(&self) -> Span {
self.fresh_start
}
pub fn replace_fresh_start(&mut self, span: Span) -> Span {
mem::replace(&mut self.fresh_start, span)
}
pub fn expr_prec(&self) -> ExprPrec {
self.expr_prec
}
pub fn replace_expr_prec(&mut self, prec: ExprPrec) -> ExprPrec {
mem::replace(&mut self.expr_prec, prec)
}
fn new_to_old_id<Id: MappableId>(&self, id: Id) -> Id {
id.map_id(self)
}
pub fn enter<'b>(&'b mut self, rw: &'b mut TextRewrite) -> RewriteCtxtRef<'s, 'b> {
RewriteCtxtRef { cx: self, rw }
}
pub fn text_span(&mut self, s: &str) -> Span {
if let Some(&sp) = self.text_span_cache.get(s) {
return sp;
}
let sp = driver::make_span_for_text(self.sess.source_map(), s);
self.text_span_cache.insert(s.to_owned(), sp);
sp
}
}
pub struct RewriteCtxtRef<'s: 'a, 'a> {
cx: &'a mut RewriteCtxt<'s>,
rw: &'a mut TextRewrite,
}
impl<'s, 'a> Deref for RewriteCtxtRef<'s, 'a> {
type Target = RewriteCtxt<'s>;
fn deref(&self) -> &RewriteCtxt<'s> {
self.cx
}
}
impl<'s, 'a> DerefMut for RewriteCtxtRef<'s, 'a> {
fn deref_mut(&mut self) -> &mut RewriteCtxt<'s> {
self.cx
}
}
impl<'s, 'a> RewriteCtxtRef<'s, 'a> {
pub fn borrow<'b>(&'b mut self) -> RewriteCtxtRef<'s, 'b> {
RewriteCtxtRef {
cx: self.cx,
rw: self.rw,
}
}
pub fn enter<'b>(&'b mut self, rw: &'b mut TextRewrite) -> RewriteCtxtRef<'s, 'b> {
RewriteCtxtRef { cx: self.cx, rw }
}
pub fn mark(&self) -> (usize, usize) {
(self.rw.rewrites.len(), self.rw.nodes.len())
}
pub fn rewind(&mut self, mark: (usize, usize)) {
self.rw.rewrites.truncate(mark.0);
self.rw.nodes.truncate(mark.1);
}
pub fn record(&mut self, rw: TextRewrite) {
self.rw.rewrites.push(rw);
}
pub fn record_text(&mut self, old_span: Span, text: &str) {
let new_span = self.text_span(text);
self.record(TextRewrite::new(old_span, new_span));
}
pub fn record_node_span(&mut self, span: Span, id: NodeId) {
self.rw.nodes.push((span, id));
}
}
pub fn rewrite<'s, T>(
sess: &Session,
old: &'s T,
new: &T,
comment_map: &CommentMap,
node_id_map: HashMap<NodeId, NodeId>,
map_extra_ast: impl FnOnce(&mut AstMap<'s>),
) -> TextRewrite
where
T: Rewrite + Visit + GetSpan,
{
let mut map = map_ast(old);
map_extra_ast(&mut map);
let mut rw = TextRewrite::new(DUMMY_SP, old.get_span());
let mut rcx = RewriteCtxt::new(sess, map, comment_map, node_id_map);
let ok = Rewrite::rewrite(old, new, rcx.enter(&mut rw));
assert!(ok, "rewriting did not complete");
rw
}