use rustc::ty;
use syntax::ast::*;
use syntax::ptr::P;
use smallvec::smallvec;
use crate::ast_manip::{fold_blocks, FlatMapNodes, AstEquiv};
use crate::command::{CommandState, Registry};
use crate::driver::{Phase, parse_expr};
use crate::matcher::{mut_visit_match, Subst};
use crate::path_edit::fold_resolved_paths;
use crate::transform::Transform;
use c2rust_ast_builder::{mk, IntoSymbol};
use crate::RefactorCtxt;
pub struct AssignToUpdate;
impl Transform for AssignToUpdate {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let pat = parse_expr(cx.session(), "__x.__f = __y");
let repl = parse_expr(cx.session(), "__x = __s { __f: __y, .. __x }");
mut_visit_match(st, cx, pat, krate, |orig, mut mcx| {
let x = mcx.bindings.get::<_, P<Expr>>("__x").unwrap().clone();
let struct_def_id = match cx.node_type(x.id).kind {
ty::TyKind::Adt(ref def, _) => def.did,
_ => return,
};
let struct_path = cx.def_path(struct_def_id);
mcx.bindings.add("__s", struct_path);
*orig = repl.clone().subst(st, cx, &mcx.bindings);
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct MergeUpdates;
impl Transform for MergeUpdates {
fn transform(&self, krate: &mut Crate, _st: &CommandState, _cx: &RefactorCtxt) {
fold_blocks(krate, |curs| {
loop {
curs.advance_until(|s| is_struct_update(s));
if curs.eof() {
break;
}
let (path, mut fields, base) = unpack_struct_update(curs.remove());
while !curs.eof() && is_struct_update_for(curs.next(), &base) {
let (_, mut more_fields, _) = unpack_struct_update(curs.remove());
fields.append(&mut more_fields)
}
curs.insert(build_struct_update(path, fields, base))
}
})
}
}
fn is_struct_update(s: &Stmt) -> bool {
let e = match_or!([s.kind] StmtKind::Semi(ref e) => e; return false);
let (lhs, rhs) = match_or!([e.kind] ExprKind::Assign(ref lhs, ref rhs) => (lhs, rhs);
return false);
match_or!([rhs.kind] ExprKind::Struct(_, _, Some(ref base)) => lhs.ast_equiv(base);
return false)
}
fn is_struct_update_for(s: &Stmt, base1: &Expr) -> bool {
let e = match_or!([s.kind] StmtKind::Semi(ref e) => e; return false);
let rhs = match_or!([e.kind] ExprKind::Assign(_, ref rhs) => rhs;
return false);
match_or!([rhs.kind] ExprKind::Struct(_, _, Some(ref base)) => base1.ast_equiv(base);
return false)
}
fn unpack_struct_update(s: Stmt) -> (Path, Vec<Field>, P<Expr>) {
let e = expect!([s.kind] StmtKind::Semi(e) => e);
let rhs = expect!([e.into_inner().kind] ExprKind::Assign(_, rhs) => rhs);
expect!([rhs.into_inner().kind]
ExprKind::Struct(path, fields, Some(base)) => (path, fields, base))
}
fn build_struct_update(path: Path, fields: Vec<Field>, base: P<Expr>) -> Stmt {
mk().semi_stmt(
mk().assign_expr(
&base,
mk().struct_expr_base(path, fields, Some(&base))))
}
pub struct Rename(pub String);
impl Transform for Rename {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let new_ident = Ident::with_dummy_span((&self.0 as &str).into_symbol());
let mut target_def_id = None;
FlatMapNodes::visit(krate, |i: P<Item>| {
if target_def_id.is_some() || !st.marked(i.id, "target") {
return smallvec![i];
}
if !is_struct(&i) {
return smallvec![i];
}
target_def_id = Some(cx.node_def_id(i.id));
smallvec![i.map(|i| {
Item {
ident: new_ident,
.. i
}
})]
});
let target_def_id = target_def_id
.expect("found no struct to rename");
fold_resolved_paths(krate, cx, |qself, mut path, def| {
if let Some(def_id) = def[0].opt_def_id() {
if def_id == target_def_id {
path.segments.last_mut().unwrap().ident = new_ident;
}
}
(qself, path)
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
fn is_struct(i: &Item) -> bool {
if let ItemKind::Struct(ref vd, _) = i.kind {
if let VariantData::Struct(..) = *vd {
return true;
}
}
false
}
pub fn register_commands(reg: &mut Registry) {
use super::mk;
reg.register("struct_assign_to_update", |_args| mk(AssignToUpdate));
reg.register("struct_merge_updates", |_args| mk(MergeUpdates));
reg.register("rename_struct", |args| mk(Rename(args[0].clone())));
}