use rustc::session::Session;
use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi::Abi;
use std::fmt::Debug;
use std::fs;
use std::path;
use std::rc::Rc;
use syntax::ast::*;
use syntax::attr;
use syntax_pos::hygiene::SyntaxContext;
use syntax::util::comments::CommentStyle;
use syntax::token::{BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
use syntax::token::{Lit as TokenLit, LitKind as TokenLitKind};
use syntax::ptr::P;
use syntax::source_map::{BytePos, FileName, SourceFile, Span, Spanned};
use syntax::symbol::Symbol;
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
use syntax::util::parser;
use syntax::ThinVec;
use syntax_pos::DUMMY_SP;
use c2rust_ast_printer::pprust::{self, PrintState};
use crate::ast_manip::NodeTable;
use crate::ast_manip::util::extend_span_attrs;
use crate::ast_manip::{AstDeref, GetSpan, MaybeGetNodeId};
use crate::driver;
use crate::rewrite::base::{binop_left_prec, binop_right_prec};
use crate::rewrite::base::{describe, extend_span_comments, extend_span_comments_strict, is_rewritable, rewind_span_over_whitespace};
use crate::rewrite::{ExprPrec, Rewrite, RewriteCtxt, RewriteCtxtRef, TextAdjust, TextRewrite};
use crate::util::Lone;
pub trait PrintParse {
fn to_string(&self) -> String;
type Parsed: AstDeref<Target = Self>;
fn parse(sess: &Session, src: &str) -> Self::Parsed;
}
impl PrintParse for Expr {
fn to_string(&self) -> String {
pprust::expr_to_string(self)
}
type Parsed = P<Expr>;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_expr(sess, src)
}
}
impl PrintParse for Pat {
fn to_string(&self) -> String {
pprust::pat_to_string(self)
}
type Parsed = P<Pat>;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_pat(sess, src)
}
}
impl PrintParse for Ty {
fn to_string(&self) -> String {
pprust::ty_to_string(self)
}
type Parsed = P<Ty>;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_ty(sess, src)
}
}
impl PrintParse for Stmt {
fn to_string(&self) -> String {
match self.kind {
StmtKind::Expr(ref expr) => pprust::expr_to_string(expr),
_ => pprust::stmt_to_string(self),
}
}
type Parsed = Stmt;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_stmts(sess, src).lone()
}
}
impl PrintParse for Item {
fn to_string(&self) -> String {
pprust::item_to_string(self)
}
type Parsed = P<Item>;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_items(sess, src).lone()
}
}
impl PrintParse for ForeignItem {
fn to_string(&self) -> String {
pprust::to_string(|s| s.print_foreign_item(self))
}
type Parsed = ForeignItem;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_foreign_items(sess, src).lone()
}
}
impl PrintParse for Block {
fn to_string(&self) -> String {
pprust::block_to_string(self)
}
type Parsed = P<Block>;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_block(sess, src)
}
}
impl PrintParse for Param {
fn to_string(&self) -> String {
pprust::param_to_string(self)
}
type Parsed = Param;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::parse_arg(sess, src)
}
}
impl PrintParse for Attribute {
fn to_string(&self) -> String {
pprust::attribute_to_string(self)
}
type Parsed = Attribute;
fn parse(sess: &Session, src: &str) -> Self::Parsed {
driver::run_parser(sess, src, |p| {
match p.token.kind {
TokenKind::DocComment(s) => {
assert!(src.ends_with('\n'));
let span = p.token.span.with_hi(p.token.span.hi() + BytePos(1));
let attr = attr::mk_doc_comment(AttrStyle::Outer, s, span);
p.bump();
return Ok(attr);
}
_ => p.parse_attribute(true),
}
})
}
}
pub trait Splice {
fn splice_span(&self) -> Span;
fn get_adjustment(&self, _rcx: &RewriteCtxt) -> TextAdjust {
TextAdjust::None
}
}
impl Splice for Expr {
fn splice_span(&self) -> Span {
extend_span_attrs(self.span, &self.attrs)
}
fn get_adjustment(&self, rcx: &RewriteCtxt) -> TextAdjust {
let prec = self.precedence();
let need_parens = match rcx.expr_prec() {
ExprPrec::Normal(min_prec) => prec.order() < min_prec,
ExprPrec::Cond(min_prec) => {
prec.order() < min_prec || parser::contains_exterior_struct_lit(self)
}
ExprPrec::Callee(min_prec) => match self.kind {
ExprKind::Field(..) => true,
_ => prec.order() < min_prec,
},
ExprPrec::LeftLess(min_prec) => match self.kind {
ExprKind::Cast(..) | ExprKind::Type(..) => true,
_ => prec.order() < min_prec,
},
};
if need_parens {
TextAdjust::Parenthesize
} else {
TextAdjust::None
}
}
}
impl Splice for Pat {
fn splice_span(&self) -> Span {
self.span
}
}
impl Splice for Ty {
fn splice_span(&self) -> Span {
self.span
}
}
impl Splice for Stmt {
fn splice_span(&self) -> Span {
self.span
}
}
impl Splice for Item {
fn splice_span(&self) -> Span {
extend_span_attrs(self.span, &self.attrs)
}
}
impl Splice for ForeignItem {
fn splice_span(&self) -> Span {
extend_span_attrs(self.span, &self.attrs)
}
}
impl Splice for Block {
fn splice_span(&self) -> Span {
self.span
}
}
impl Splice for Param {
fn splice_span(&self) -> Span {
self.pat.span.to(self.ty.span)
}
}
impl Splice for Attribute {
fn splice_span(&self) -> Span {
self.span
}
}
pub trait Recover {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self>;
}
impl Recover for Expr {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().exprs
}
}
impl Recover for Pat {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().pats
}
}
impl Recover for Ty {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().tys
}
}
impl Recover for Stmt {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().stmts
}
}
impl Recover for Item {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().items
}
}
impl Recover for ForeignItem {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().foreign_items
}
}
impl Recover for Block {
fn node_table<'a, 's>(rcx: &'a RewriteCtxt<'s>) -> &'a NodeTable<'s, Self> {
&rcx.old_nodes().blocks
}
}
pub trait RecoverChildren: Debug {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef);
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef);
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef);
}
impl<T: RecoverChildren> RecoverChildren for P<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_children(reparsed, new, rcx)
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_and_children(reparsed, new, rcx)
}
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_restricted(old_span, reparsed, new, rcx)
}
}
impl<T: RecoverChildren> RecoverChildren for Rc<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_children(reparsed, new, rcx)
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_and_children(reparsed, new, rcx)
}
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_restricted(old_span, reparsed, new, rcx)
}
}
impl<T: RecoverChildren> RecoverChildren for Spanned<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_children(&reparsed.node, &new.node, rcx)
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_and_children(&reparsed.node, &new.node, rcx)
}
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<T as RecoverChildren>::recover_node_restricted(old_span, &reparsed.node, &new.node, rcx)
}
}
impl<T: RecoverChildren> RecoverChildren for Option<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
match (reparsed, new) {
(&Some(ref x1), &Some(ref x2)) => {
RecoverChildren::recover_children(x1, x2, rcx);
}
(_, _) => {}
}
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
match (reparsed, new) {
(&Some(ref x1), &Some(ref x2)) => {
RecoverChildren::recover_node_and_children(x1, x2, rcx);
}
(_, _) => {}
}
}
fn recover_node_restricted(_old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
RecoverChildren::recover_children(reparsed, new, rcx);
}
}
impl<A: RecoverChildren, B: RecoverChildren> RecoverChildren for (A, B) {
fn recover_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
<A as RecoverChildren>::recover_children(&reparsed.0, &new.0, rcx.borrow());
<B as RecoverChildren>::recover_children(&reparsed.1, &new.1, rcx.borrow());
}
fn recover_node_and_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
<A as RecoverChildren>::recover_node_and_children(&reparsed.0, &new.0, rcx.borrow());
<B as RecoverChildren>::recover_node_and_children(&reparsed.1, &new.1, rcx.borrow());
}
fn recover_node_restricted(_old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
RecoverChildren::recover_children(reparsed, new, rcx);
}
}
impl<A: RecoverChildren, B: RecoverChildren, C: RecoverChildren> RecoverChildren for (A, B, C) {
fn recover_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
<A as RecoverChildren>::recover_children(&reparsed.0, &new.0, rcx.borrow());
<B as RecoverChildren>::recover_children(&reparsed.1, &new.1, rcx.borrow());
<C as RecoverChildren>::recover_children(&reparsed.2, &new.2, rcx.borrow());
}
fn recover_node_and_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
<A as RecoverChildren>::recover_node_and_children(&reparsed.0, &new.0, rcx.borrow());
<B as RecoverChildren>::recover_node_and_children(&reparsed.1, &new.1, rcx.borrow());
<C as RecoverChildren>::recover_node_and_children(&reparsed.2, &new.2, rcx.borrow());
}
fn recover_node_restricted(_old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
RecoverChildren::recover_children(reparsed, new, rcx);
}
}
impl<T: RecoverChildren> RecoverChildren for [T] {
fn recover_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
assert!(
reparsed.len() == new.len(),
"new and reparsed ASTs don't match: {:?} != {:?}",
new, reparsed
);
for i in 0..reparsed.len() {
RecoverChildren::recover_children(&reparsed[i], &new[i], rcx.borrow());
}
}
fn recover_node_and_children(reparsed: &Self, new: &Self, mut rcx: RewriteCtxtRef) {
assert!(
reparsed.len() == new.len(),
"new and reparsed ASTs don't match: {:?} != {:?}",
new, reparsed
);
for i in 0..reparsed.len() {
RecoverChildren::recover_node_and_children(&reparsed[i], &new[i], rcx.borrow());
}
}
fn recover_node_restricted(_old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
RecoverChildren::recover_children(reparsed, new, rcx);
}
}
impl<T: RecoverChildren> RecoverChildren for Vec<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_children(&reparsed, &new, rcx)
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_node_and_children(&reparsed, &new, rcx)
}
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_node_restricted(old_span, &reparsed, &new, rcx)
}
}
impl<T: RecoverChildren> RecoverChildren for ThinVec<T> {
fn recover_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_children(&reparsed, &new, rcx)
}
fn recover_node_and_children(reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_node_and_children(&reparsed, &new, rcx)
}
fn recover_node_restricted(old_span: Span, reparsed: &Self, new: &Self, rcx: RewriteCtxtRef) {
<[T] as RecoverChildren>::recover_node_restricted(old_span, &reparsed, &new, rcx)
}
}
include!(concat!(
env!("OUT_DIR"),
"/rewrite_recover_children_gen.inc.rs"
));
fn recover<'s, T>(
maybe_restricted_span: Option<Span>,
reparsed: &T,
new: &T,
mut rcx: RewriteCtxtRef<'s, '_>,
) -> bool
where
T: MaybeGetNodeId + Recover + Rewrite + Splice + 's,
{
let old_id = rcx.new_to_old_id(new.get_node_id());
let old = match <T as Recover>::node_table(&rcx).get(old_id) {
Some(x) => x,
None => {
return false;
}
};
let old_span = rewind_span_over_whitespace(old.splice_span(), &rcx);
let old_span = match extend_span_comments_strict(&old_id, old_span, &rcx) {
Ok(span) => span,
Err(_) => return false,
};
let reparsed_span = extend_span_comments(&old_id, reparsed.splice_span(), &rcx);
if !is_rewritable(old_span) {
return false;
}
let sf = rcx
.session()
.source_map()
.lookup_byte_offset(old_span.lo())
.sf;
if let FileName::Macros(..) = sf.name {
return false;
}
if let Some(restricted_span) = maybe_restricted_span {
if old.splice_span() == restricted_span {
return false;
}
}
info!("REVERT {}", describe(rcx.session(), reparsed_span));
info!(" TO {}", describe(rcx.session(), old_span));
let mut rw = TextRewrite::adjusted(
reparsed_span,
old_span,
new.get_adjustment(&rcx),
);
let mark = rcx.mark();
let ok = Rewrite::rewrite(old, new, rcx.enter(&mut rw));
if !ok {
rcx.rewind(mark);
return false;
}
rcx.record(rw);
true
}
pub fn rewrite<T>(old: &T, new: &T, rcx: RewriteCtxtRef) -> bool
where
T: PrintParse + RecoverChildren + Splice + MaybeGetNodeId,
{
if !is_rewritable(old.splice_span()) {
warn!("can't splice in fresh text for a non-rewritable node");
return true;
}
new.rewrite_at(old.splice_span(), rcx)
}
fn describe_rewrite(old_span: Span, new_span: Span, rcx: &RewriteCtxt) {
if old_span.lo() != old_span.hi() {
info!("REWRITE {}", describe(rcx.session(), old_span));
info!(
" INTO {}",
describe(rcx.session(), new_span)
);
} else {
info!("INSERT AT {}", describe(rcx.session(), old_span));
info!(
" TEXT {}",
describe(rcx.session(), new_span)
);
}
}
fn add_comments<T>(s: String, node: &T, rcx: &RewriteCtxt) -> String
where T: MaybeGetNodeId
{
if <T as MaybeGetNodeId>::supported() {
if let Some(comments) = rcx.comments().get(&rcx.new_to_old_id(node.get_node_id())) {
let mut new_s = String::new();
let mut sorted_comments = comments.iter().collect::<Vec<_>>();
sorted_comments.sort_by_key(|c| c.pos);
for comment in &sorted_comments {
if comment.style == CommentStyle::Isolated {
new_s.push('\n');
comment.lines.iter().for_each(|s| {
new_s.push_str(s.as_str());
new_s.push('\n');
});
}
}
new_s.push_str(&s);
for comment in &sorted_comments {
if comment.style == CommentStyle::Trailing {
comment.lines.iter().for_each(|s| {
new_s.push_str(s.as_str());
new_s.push('\n');
});
}
}
return new_s;
}
}
s
}
fn rewrite_at_impl<T>(old_span: Span, new: &T, mut rcx: RewriteCtxtRef) -> bool
where
T: PrintParse + RecoverChildren + Splice + MaybeGetNodeId,
{
let printed = add_comments(new.to_string(), new, &rcx);
let reparsed = T::parse(rcx.session(), &printed);
let reparsed = reparsed.ast_deref();
let mut expanded_old_span = old_span;
let mut reparsed_span = reparsed.splice_span();
if <T as MaybeGetNodeId>::supported() {
let old_id = rcx.new_to_old_id(new.get_node_id());
expanded_old_span = extend_span_comments(&old_id, old_span, &rcx);
reparsed_span = match extend_span_comments_strict(&old_id, reparsed_span, &rcx) {
Ok(span) => rewind_span_over_whitespace(span, &rcx),
Err(_) => return false,
};
}
describe_rewrite(expanded_old_span, reparsed_span, &rcx);
let mut rw = TextRewrite::adjusted(expanded_old_span, reparsed_span, new.get_adjustment(&rcx));
RecoverChildren::recover_node_restricted(old_span, reparsed, new, rcx.enter(&mut rw));
rcx.record(rw);
true
}
pub trait RewriteAt {
fn rewrite_at(&self, old_span: Span, rcx: RewriteCtxtRef) -> bool;
}
impl<T> RewriteAt for T
where T: PrintParse + RecoverChildren + Splice + MaybeGetNodeId
{
default fn rewrite_at(&self, old_span: Span, rcx: RewriteCtxtRef) -> bool {
rewrite_at_impl(old_span, self, rcx)
}
}
fn create_file_for_module(
module_item: &Item,
old_span: Span,
sess: &Session,
) -> (Lrc<SourceFile>, Option<Attribute>) {
let source_map = sess.source_map();
let old_sf = source_map.lookup_byte_offset(old_span.lo()).sf;
let mut path_attr = None;
let filename = match old_sf.name.clone() {
FileName::Real(mut path) => {
let mod_file_name = format!("{}.rs", module_item.ident.to_string());
if let Some(path_attr) = attr::first_attr_value_str_by_name(&module_item.attrs, Symbol::intern("path")) {
path.pop();
path.push(path_attr.to_string());
} else {
if sess.local_crate_source_file.as_ref().map_or(false, |f| *f == path) {
path.pop();
if path.file_name().map_or(true, |path| path != "src") {
path.push("src");
let _ = fs::create_dir_all(&path);
let path_item = attr::mk_name_value_item_str(
Ident::from_str("path"),
Symbol::intern(&format!(
"src{}{}",
path::MAIN_SEPARATOR,
mod_file_name,
)),
DUMMY_SP,
);
path_attr = Some(attr::mk_attr_outer(path_item));
}
} else {
if path.file_name().unwrap() == "mod.rs" {
path.pop();
} else {
let parent_name = path.file_stem().unwrap().to_os_string();
path.pop();
path.push(parent_name);
}
}
}
path.push(mod_file_name);
path
}
_ => panic!("Could not construct file path for external module {:?}", module_item.ident),
};
let sf = source_map.new_source_file(FileName::Real(filename), String::new());
(sf, path_attr)
}
impl RewriteAt for Item {
fn rewrite_at(&self, old_span: Span, mut rcx: RewriteCtxtRef) -> bool {
if let ItemKind::Mod(module) = &self.kind {
if !module.inline {
if module.items.is_empty() {
info!("DELETE {}", describe(rcx.session(), old_span));
rcx.record(TextRewrite::new(old_span, DUMMY_SP));
return true;
}
let mut item = self.clone();
let inner_span = if !is_rewritable(module.inner) {
let (sf, path_attr) = create_file_for_module(self, old_span, rcx.session());
if let Some(attr) = path_attr {
item.attrs.push(attr);
}
Span::new(sf.start_pos, sf.end_pos, SyntaxContext::root())
} else {
module.inner
};
let printed = add_comments(item.to_string(), &item, &rcx);
let reparsed = Self::parse(rcx.session(), &printed);
let reparsed = reparsed.ast_deref();
describe_rewrite(old_span, reparsed.splice_span(), &rcx);
let rw = TextRewrite::adjusted(
old_span,
reparsed.splice_span(),
self.get_adjustment(&rcx),
);
rcx.record(rw);
let mut printed = pprust::to_string(|s| s.print_inner_attributes(&self.attrs));
for item in &module.items {
printed.push_str(&add_comments(item.to_string(), item, &rcx));
}
let reparsed = driver::parse_items(rcx.session(), &printed);
let first_node = reparsed.first().unwrap();
let first_span = extend_span_comments(&first_node.get_node_id(), first_node.splice_span(), &rcx);
let last_node = reparsed.last().unwrap();
let last_span = extend_span_comments(&last_node.get_node_id(), last_node.splice_span(), &rcx);
let reparsed_span = first_span.with_hi(last_span.hi());
describe_rewrite(inner_span, reparsed_span, &rcx);
let mut rw = TextRewrite::adjusted(
inner_span,
reparsed_span,
self.get_adjustment(&rcx),
);
RecoverChildren::recover_children(&reparsed, &module.items, rcx.enter(&mut rw));
rcx.record(rw);
return true;
}
}
rewrite_at_impl(old_span, self, rcx)
}
}