use rustc_target::spec::abi::Abi;
use syntax::ast::*;
use syntax::parse::token::{DelimToken, Nonterminal, Token};
use syntax::source_map::{Span, SyntaxContext};
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
use syntax::ThinVec;
use diff;
use rustc::session::Session;
use std::fmt::Debug;
use std::rc::Rc;
use syntax::ptr::P;
use syntax::source_map::{Spanned, DUMMY_SP};
use syntax::util::parser::{AssocOp, Fixity};
use crate::ast_manip::{AstDeref, GetSpan};
use super::strategy;
use super::strategy::print;
use super::{ExprPrec, RewriteCtxtRef, SeqItemId, TextRewrite};
pub trait Rewrite {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool;
}
include!(concat!(env!("OUT_DIR"), "/rewrite_rewrite_gen.inc.rs"));
impl<T: Rewrite> Rewrite for P<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
<T as Rewrite>::rewrite(old, new, rcx)
}
}
impl<T: Rewrite> Rewrite for Rc<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
<T as Rewrite>::rewrite(old, new, rcx)
}
}
impl<T: Rewrite> Rewrite for Spanned<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
<T as Rewrite>::rewrite(&old.node, &new.node, rcx)
}
}
impl<T: Rewrite> Rewrite for Option<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
match (old, new) {
(&Some(ref x1), &Some(ref x2)) => Rewrite::rewrite(x1, x2, rcx),
(&None, &None) => true,
(_, _) => false,
}
}
}
impl<A: Rewrite, B: Rewrite> Rewrite for (A, B) {
fn rewrite(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {
<A as Rewrite>::rewrite(&old.0, &new.0, rcx.borrow())
&& <B as Rewrite>::rewrite(&old.1, &new.1, rcx.borrow())
&& true
}
}
impl<A: Rewrite, B: Rewrite, C: Rewrite> Rewrite for (A, B, C) {
fn rewrite(old: &Self, new: &Self, mut rcx: RewriteCtxtRef) -> bool {
<A as Rewrite>::rewrite(&old.0, &new.0, rcx.borrow())
&& <B as Rewrite>::rewrite(&old.1, &new.1, rcx.borrow())
&& <C as Rewrite>::rewrite(&old.2, &new.2, rcx.borrow())
&& true
}
}
impl<T: MaybeRewriteSeq> Rewrite for [T] {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
MaybeRewriteSeq::maybe_rewrite_seq(old, new, DUMMY_SP, rcx)
}
}
impl<T: MaybeRewriteSeq> Rewrite for Vec<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
<[T] as Rewrite>::rewrite(&old, &new, rcx)
}
}
impl<T: MaybeRewriteSeq> Rewrite for ThinVec<T> {
fn rewrite(old: &Self, new: &Self, rcx: RewriteCtxtRef) -> bool {
<[T] as Rewrite>::rewrite(&old, &new, rcx)
}
}
pub trait SeqItem {
fn seq_item_id(&self) -> SeqItemId;
}
include!(concat!(env!("OUT_DIR"), "/rewrite_seq_item_gen.inc.rs"));
impl SeqItem for Attribute {
fn seq_item_id(&self) -> SeqItemId {
SeqItemId::Attr(self.id)
}
}
impl SeqItem for Arg {
fn seq_item_id(&self) -> SeqItemId {
SeqItemId::Node(self.id)
}
}
impl<T: SeqItem> SeqItem for P<T> {
fn seq_item_id(&self) -> SeqItemId {
<T as SeqItem>::seq_item_id(self)
}
}
pub trait MaybeRewriteSeq: Rewrite + Sized {
fn maybe_rewrite_seq(
old: &[Self],
new: &[Self],
_outer_span: Span,
rcx: RewriteCtxtRef,
) -> bool {
rewrite_seq_unsupported(old, new, rcx)
}
}
include!(concat!(
env!("OUT_DIR"),
"/rewrite_maybe_rewrite_seq_gen.inc.rs"
));
impl<T: Rewrite> MaybeRewriteSeq for Spanned<T> {}
impl<A: Rewrite, B: Rewrite> MaybeRewriteSeq for (A, B) {}
pub fn rewrite_seq_unsupported<T: Rewrite>(old: &[T], new: &[T], mut rcx: RewriteCtxtRef) -> bool {
if old.len() != new.len() {
return false;
} else {
for i in 0..old.len() {
if !Rewrite::rewrite(&old[i], &new[i], rcx.borrow()) {
return false;
}
}
true
}
}
pub fn rewrite_seq<T, R>(old: &[R], new: &[R], outer_span: Span, mut rcx: RewriteCtxtRef) -> bool
where
T: SeqItem + print::Splice + print::PrintParse + print::RecoverChildren + Rewrite + Debug,
R: AstDeref<Target = T>,
{
if old.len() == 0 && new.len() != 0 && !is_rewritable(outer_span) {
return false;
}
fn ast<T: AstDeref>(x: &T) -> &<T as AstDeref>::Target {
x.ast_deref()
}
let new_ids = new
.iter()
.map(|x| rcx.new_to_old_id(ast(x).seq_item_id()))
.collect::<Vec<_>>();
let old_ids = old.iter().map(|x| ast(x).seq_item_id()).collect::<Vec<_>>();
let mut i = 0;
let mut j = 0;
for step in diff::slice(&old_ids, &new_ids) {
match step {
diff::Result::Left(_) => {
info!(
"DELETE {}",
describe(rcx.session(), ast(&old[i]).splice_span())
);
rcx.record(TextRewrite::new(ast(&old[i]).splice_span(), DUMMY_SP));
i += 1;
}
diff::Result::Right(_) => {
let before = if i > 0 {
ast(&old[i - 1]).splice_span()
} else {
outer_span.shrink_to_lo()
};
let after = if i < old.len() {
ast(&old[i]).splice_span()
} else {
outer_span.shrink_to_hi()
};
let old_span = if is_rewritable(before) {
before.with_lo(before.hi())
} else if is_rewritable(after) {
after.with_hi(after.lo())
} else {
warn!("can't insert new node between two non-rewritable nodes");
return true;
};
let ok = strategy::print::rewrite_at(old_span, ast(&new[j]), rcx.borrow());
if !ok {
return false;
}
j += 1;
}
diff::Result::Both(_, _) => {
let ok = Rewrite::rewrite(ast(&old[i]), ast(&new[j]), rcx.borrow());
if !ok {
return false;
}
i += 1;
j += 1;
}
}
}
true
}
pub fn calc_outer_span<T: GetSpan>(seq: &[T], default: Span) -> Span {
let mut sp = default;
for node in seq {
let node_sp = node.get_span();
if is_rewritable(node_sp) {
sp = sp.to(node_sp);
}
}
sp
}
pub fn rewrite_seq_comma_sep<T, R>(
old: &[R],
new: &[R],
old_spans: &[Span],
outer_span: Span,
has_trailing_comma: bool,
mut rcx: RewriteCtxtRef,
) -> bool
where
T: SeqItem + print::Splice + print::PrintParse + print::RecoverChildren + Rewrite + Debug,
R: AstDeref<Target = T>,
{
fn ast<T: AstDeref>(x: &T) -> &<T as AstDeref>::Target {
x.ast_deref()
}
let new_ids = new
.iter()
.map(|x| rcx.new_to_old_id(ast(x).seq_item_id()))
.collect::<Vec<_>>();
let old_ids = old.iter().map(|x| ast(x).seq_item_id()).collect::<Vec<_>>();
let mut i = 0;
let mut j = 0;
let mut comma_before = true;
for step in diff::slice(&old_ids, &new_ids) {
match step {
diff::Result::Left(_) => {
info!("DELETE {}", describe(rcx.session(), old_spans[i]));
rcx.record(TextRewrite::new(old_spans[i], DUMMY_SP));
i += 1;
if i == old.len() && !has_trailing_comma {
comma_before = false;
}
}
diff::Result::Right(_) => {
let before = if i > 0 {
old_spans[i - 1]
} else {
outer_span.shrink_to_lo()
};
let after = if i < old.len() {
old_spans[i]
} else {
outer_span.shrink_to_hi()
};
let old_span = if is_rewritable(before) {
before.with_lo(before.hi())
} else if is_rewritable(after) {
after.with_hi(after.lo())
} else {
warn!("can't insert new node between two non-rewritable nodes");
return false;
};
if !comma_before {
rcx.record_text(old_span, ", ");
}
let ok = strategy::print::rewrite_at(old_span, ast(&new[j]), rcx.borrow());
if !ok {
return false;
}
j += 1;
if j == new.len() {
comma_before = false;
} else {
rcx.record_text(old_span, ", ");
comma_before = true;
}
}
diff::Result::Both(_, _) => {
let ok = Rewrite::rewrite(ast(&old[i]), ast(&new[j]), rcx.borrow());
if !ok {
return false;
}
i += 1;
j += 1;
if i == old.len() && !has_trailing_comma {
comma_before = false;
}
}
}
}
true
}
pub fn binop_left_prec(op: &BinOp) -> ExprPrec {
let assoc_op = AssocOp::from_ast_binop(op.node);
let prec = assoc_op.precedence() as i8;
let fixity = assoc_op.fixity();
let prec = match fixity {
Fixity::Left => prec,
Fixity::Right => prec + 1,
Fixity::None => prec + 1,
};
match assoc_op {
AssocOp::Less | AssocOp::LessEqual | AssocOp::ObsoleteInPlace => ExprPrec::LeftLess(prec),
_ => ExprPrec::Normal(prec),
}
}
pub fn binop_right_prec(op: &BinOp) -> ExprPrec {
let assoc_op = AssocOp::from_ast_binop(op.node);
let prec = assoc_op.precedence() as i8;
let fixity = assoc_op.fixity();
let prec = match fixity {
Fixity::Left => prec + 1,
Fixity::Right => prec,
Fixity::None => prec + 1,
};
ExprPrec::Normal(prec)
}
pub fn is_rewritable(sp: Span) -> bool {
sp != DUMMY_SP &&
sp.ctxt() == SyntaxContext::empty()
}
pub fn describe(sess: &Session, span: Span) -> String {
let cm = sess.source_map();
let lo = cm.lookup_byte_offset(span.lo());
let hi = cm.lookup_byte_offset(span.hi());
let src = &lo.sf.src.as_ref().unwrap()[lo.pos.0 as usize..hi.pos.0 as usize];
if Rc::ptr_eq(&lo.sf, &hi.sf) {
format!("{}: {} .. {} = {}", lo.sf.name, lo.pos.0, hi.pos.0, src)
} else {
format!(
"{}: {} .. {}: {} = {}",
lo.sf.name, lo.pos.0, hi.sf.name, hi.pos.0, src
)
}
}