use std::collections::{HashMap, HashSet};
use rustc::hir::def_id::DefId;
use rustc::ty::TyKind;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::ast::*;
use syntax::attr;
use syntax::mut_visit::{self, MutVisitor};
use syntax::ptr::P;
use smallvec::SmallVec;
use c2rust_ast_builder::{mk, IntoSymbol};
use crate::ast_manip::{FlatMapNodes, MutVisitNodes, fold_modules, visit_nodes, MutVisit};
use crate::command::{CommandState, Registry};
use crate::driver::{Phase, parse_expr};
use crate::matcher::{BindingType, MatchCtxt, Subst, mut_visit_match_with};
use crate::path_edit::{fold_resolved_paths, fold_resolved_paths_with_id};
use crate::transform::Transform;
use crate::util::Lone;
use crate::RefactorCtxt;
pub struct ToMethod;
impl Transform for ToMethod {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let mut dest = None;
FlatMapNodes::visit(krate, |i: P<Item>| {
if !st.marked(i.id, "dest") ||
!matches!([i.node] ItemKind::Impl(_, _, _, _, None, _, _)) {
return smallvec![i];
}
if dest.is_none() {
dest = Some(i.clone());
}
smallvec![i]
});
if dest.is_none() {
return;
}
let dest = dest.unwrap();
struct FnInfo {
item: P<Item>,
decl: P<FnDecl>,
header: FnHeader,
generics: Generics,
block: P<Block>,
arg_idx: Option<usize>,
}
let mut fns = Vec::new();
fold_modules(krate, |curs| {
while let Some(arg_idx) = curs.advance_until_match(|i| {
let decl = match_or!([i.node] ItemKind::Fn(ref decl, ..) => decl; return None);
for (idx, arg) in decl.inputs.iter().enumerate() {
if st.marked(arg.id, "target") {
return Some(Some(idx));
}
}
if st.marked(i.id, "target") {
return Some(None);
}
None
}) {
let i = curs.remove();
unpack!([i.node.clone()]
ItemKind::Fn(decl, header, generics, block));
fns.push(FnInfo {
item: i,
decl, header, generics, block,
arg_idx,
});
}
});
struct FnRefInfo {
ident: Ident,
arg_idx: Option<usize>,
}
let fn_ref_info = fns.iter().map(|f| {
(cx.node_def_id(f.item.id),
FnRefInfo {
ident: f.item.ident.clone(),
arg_idx: f.arg_idx,
})
}).collect::<HashMap<_, _>>();
for f in &mut fns {
let arg_idx = match_or!([f.arg_idx] Some(x) => x; continue);
let mut inputs = f.decl.inputs.clone();
let arg = inputs.remove(arg_idx);
let mode = match arg.pat.node {
PatKind::Ident(mode, _, _) => mode,
_ => panic!("unsupported argument pattern (expected ident): {:?}", arg.pat),
};
let pat_ty = cx.node_type(arg.pat.id);
let self_ty = cx.def_type(cx.node_def_id(dest.id));
let arg_hir_id = cx.hir_map().node_to_hir_id(arg.pat.id);
let self_kind = {
if pat_ty == self_ty {
match mode {
BindingMode::ByValue(mutbl) => Some(SelfKind::Value(mutbl)),
BindingMode::ByRef(mutbl) => Some(SelfKind::Region(None, mutbl)),
}
} else {
match pat_ty.sty {
TyKind::Ref(_, ty, _) if ty == self_ty => {
match arg.ty.node {
ast::TyKind::Rptr(ref lt, ref mty) =>
Some(SelfKind::Region(lt.clone(), mty.mutbl)),
_ => None,
}
},
_ => None,
}
}
};
let self_kind = match self_kind {
Some(x) => x,
None => panic!("unsupported argument type (expected {:?} or a ref): {:?}",
self_ty, pat_ty),
};
inputs.insert(0, mk().self_arg(self_kind));
f.decl = f.decl.clone().map(|fd| FnDecl { inputs: inputs, .. fd });
fold_resolved_paths(&mut f.block, cx, |qself, path, def| {
match cx.def_to_hir_id(&def) {
Some(hir_id) =>
if hir_id == arg_hir_id {
assert!(qself.is_none());
return (None, mk().path(vec!["self"]));
} else {
(qself, path)
},
None => (qself, path)
}
});
}
let mut fns = Some(fns);
FlatMapNodes::visit(krate, |i: P<Item>| {
if i.id != dest.id || fns.is_none() {
return smallvec![i];
}
smallvec![i.map(|i| {
unpack!([i.node] ItemKind::Impl(
unsafety, polarity, generics, defaultness, trait_ref, ty, items));
let mut items = items;
let fns = fns.take().unwrap();
items.extend(fns.into_iter().map(|f| {
let sig = MethodSig {
header: f.header,
decl: f.decl,
};
ImplItem {
id: DUMMY_NODE_ID,
ident: f.item.ident.clone(),
vis: f.item.vis.clone(),
defaultness: Defaultness::Final,
attrs: f.item.attrs.clone(),
generics: f.generics,
node: ImplItemKind::Method(sig, f.block),
span: f.item.span,
tokens: None,
}
}));
Item {
node: ItemKind::Impl(
unsafety, polarity, generics, defaultness, trait_ref, ty, items),
.. i
}
})]
});
MutVisitNodes::visit(krate, |e: &mut P<Expr>| {
if !matches!([e.node] ExprKind::Call(..)) {
return;
}
unpack!([e.node.clone()] ExprKind::Call(func, args));
let def_id = match_or!([cx.try_resolve_expr(&func)] Some(x) => x; return);
let info = match_or!([fn_ref_info.get(&def_id)] Some(x) => x; return);
if let Some(arg_idx) = info.arg_idx {
let mut args = args;
let self_arg = args.remove(arg_idx);
args.insert(0, self_arg);
e.node = ExprKind::MethodCall(
mk().path_segment(&info.ident),
args
);
} else {
let mut new_path = cx.def_path(cx.node_def_id(dest.id));
new_path.segments.push(mk().path_segment(&info.ident));
e.node = ExprKind::Call(mk().path_expr(new_path), args);
}
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct FixUnusedUnsafe;
impl Transform for FixUnusedUnsafe {
fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
MutVisitNodes::visit(krate, |b: &mut P<Block>| {
if let BlockCheckMode::Unsafe(UnsafeSource::UserProvided) = b.rules {
let parent = cx.hir_map().get_parent_did(b.id);
let result = cx.ty_ctxt().unsafety_check_result(parent);
let unused = result.unsafe_blocks.iter().any(|&(id, used)| {
id == cx.hir_map().node_to_hir_id(b.id) && !used
});
if unused {
b.rules = BlockCheckMode::Default;
}
}
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct SinkUnsafe;
struct SinkUnsafeFolder<'a> {
st: &'a CommandState,
}
impl<'a> MutVisitor for SinkUnsafeFolder<'a> {
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
let i = if self.st.marked(i.id, "target") {
i.map(|mut i| {
match i.node {
ItemKind::Fn(_, ref mut header, _, ref mut block) => {
sink_unsafe(&mut header.unsafety, block);
},
_ => {},
}
i
})
} else {
i
};
mut_visit::noop_flat_map_item(i, self)
}
fn flat_map_impl_item(&mut self, mut i: ImplItem) -> SmallVec<[ImplItem; 1]> {
if self.st.marked(i.id, "target") {
match i.node {
ImplItemKind::Method(MethodSig { ref mut header, .. }, ref mut block) => {
sink_unsafe(&mut header.unsafety, block);
},
_ => {},
}
}
mut_visit::noop_flat_map_impl_item(i, self)
}
}
fn sink_unsafe(unsafety: &mut Unsafety, block: &mut P<Block>) {
if *unsafety == Unsafety::Unsafe {
*unsafety = Unsafety::Normal;
*block = mk().block(vec![
mk().expr_stmt(mk().block_expr(mk().unsafe_().block(
block.stmts.clone())))]);
}
}
impl Transform for SinkUnsafe {
fn transform(&self, krate: &mut Crate, st: &CommandState, _cx: &RefactorCtxt) {
krate.visit(&mut SinkUnsafeFolder { st })
}
}
pub struct WrapExtern;
impl Transform for WrapExtern {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
#[derive(Debug)]
struct FuncInfo {
id: NodeId,
def_id: DefId,
ident: Ident,
decl: P<FnDecl>,
}
let mut fns = Vec::new();
visit_nodes(krate, |fi: &ForeignItem| {
if !st.marked(fi.id, "target") {
return;
}
match fi.node {
ForeignItemKind::Fn(ref decl, _) => {
fns.push(FuncInfo {
id: fi.id,
def_id: cx.node_def_id(fi.id),
ident: fi.ident.clone(),
decl: decl.clone(),
});
},
_ => {},
}
});
info!("found {} fns", fns.len());
for i in &fns {
info!(" {:?}", i);
}
let mut dest_path = None;
FlatMapNodes::visit(krate, |i: P<Item>| {
if !st.marked(i.id, "dest") {
return smallvec![i];
}
if dest_path.is_some() {
info!("warning: found multiple \"dest\" marks");
return smallvec![i];
}
dest_path = Some(cx.def_path(cx.node_def_id(i.id)));
smallvec![i.map(|i| {
unpack!([i.node] ItemKind::Mod(m));
let mut m = m;
for f in &fns {
let func_path = cx.def_path(cx.node_def_id(f.id));
let arg_names = f.decl.inputs.iter().enumerate().map(|(idx, arg)| {
match arg.pat.node {
PatKind::Ident(BindingMode::ByValue(Mutability::Immutable),
ident,
None) => {
ident
},
_ => {
mk().ident(format!("arg{}", idx))
},
}
}).collect::<Vec<_>>();
let wrapper_args = f.decl.inputs.iter()
.zip(arg_names.iter())
.map(|(old, name)| {
Arg {
pat: mk().ident_pat(name.clone()),
..old.clone()
}
}).collect::<Vec<_>>();
let arg_exprs = arg_names.iter().map(|name| {
mk().ident_expr(name)
}).collect::<Vec<_>>();
let decl = P(FnDecl {
inputs: wrapper_args,
output: f.decl.output.clone(),
c_variadic: false,
});
let body = mk().block(vec![
mk().expr_stmt(mk().call_expr(
mk().path_expr(func_path),
arg_exprs))]);
m.items.push(mk().pub_().unsafe_().fn_item(&f.ident, decl, body));
}
Item {
node: ItemKind::Mod(m),
.. i
}
})]
});
if dest_path.is_none() {
info!("warning: found no \"dest\" mark");
return;
}
let dest_path = dest_path.unwrap();
let ident_map = fns.iter().map(|f| (f.def_id, f.ident)).collect::<HashMap<_, _>>();
fold_resolved_paths(krate, cx, |qself, path, def| {
match def.opt_def_id() {
Some(def_id) if ident_map.contains_key(&def_id) => {
let ident = ident_map.get(&def_id).unwrap();
let mut new_path = dest_path.clone();
new_path.segments.push(mk().path_segment(ident));
(qself, new_path)
},
_ => (qself, path),
}
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct WrapApi;
impl Transform for WrapApi {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let mut wrapper_map = HashMap::new();
FlatMapNodes::visit(krate, |i: P<Item>| {
if !st.marked(i.id, "target") {
return smallvec![i];
}
if !matches!([i.node] ItemKind::Fn(..)) {
return smallvec![i];
}
let (decl, old_abi) = expect!([i.node]
ItemKind::Fn(ref decl, ref header, _, _) => (decl.clone(), header.abi));
let symbol =
if let Some(sym) = attr::first_attr_value_str_by_name(&i.attrs, "export_name") {
sym
} else if attr::contains_name(&i.attrs, "no_mangle") {
i.ident.name
} else {
warn!("marked function `{:?}` does not have a stable symbol", i.ident.name);
return smallvec![i];
};
let i = i.map(|mut i| {
i.attrs.retain(|attr| {
attr.path != "no_mangle" &&
attr.path != "export_name"
});
match i.node {
ItemKind::Fn(_, ref mut header, _, _) => header.abi = Abi::Rust,
_ => unreachable!(),
}
i
});
let mut used_names = HashSet::new();
let arg_names = decl.inputs.iter().enumerate().map(|(idx, arg)| {
let base = match arg.pat.node {
PatKind::Ident(_, ref ident, _) => ident.name,
_ => format!("arg{}", idx).into_symbol(),
};
let name;
if !used_names.contains(&base) {
name = base;
} else {
let mut i = 0;
loop {
let gen_name = format!("{}_{}", base.as_str(), i).into_symbol();
if !used_names.contains(&gen_name) {
name = gen_name;
break;
}
i += 1;
}
}
used_names.insert(name);
name
}).collect::<Vec<_>>();
let wrapper_decl = decl.clone().map(|decl| {
let new_inputs = decl.inputs.iter().zip(arg_names.iter()).map(|(arg, &name)| {
mk().arg(&arg.ty, mk().ident_pat(name))
}).collect();
FnDecl {
inputs: new_inputs,
.. decl
}
});
let wrapper_args = arg_names.iter().map(|&name| mk().ident_expr(name)).collect();
let wrapper_name = format!("{}_wrapper", symbol.as_str());
let wrapper =
mk().vis(i.vis.clone()).unsafe_().abi(old_abi)
.str_attr("export_name", symbol).fn_item(
&wrapper_name,
wrapper_decl,
mk().block(vec![
mk().expr_stmt(mk().call_expr(
mk().path_expr(vec![i.ident.name]),
wrapper_args,
))
])
);
let item_hir_id = cx.hir_map().node_to_hir_id(i.id);
wrapper_map.insert(item_hir_id, wrapper_name);
let mut v = smallvec![];
v.push(i);
v.push(wrapper);
v
});
let mut callees = HashSet::new();
visit_nodes(krate, |e: &Expr| {
if let ExprKind::Call(ref callee, _) = e.node {
callees.insert(callee.id);
}
});
fold_resolved_paths_with_id(krate, cx, |id, q, p, d| {
if callees.contains(&id) || q.is_some() {
return (q, p);
}
let hir_id = match_or!([cx.def_to_hir_id(d)] Some(x) => x; return (q, p));
let name = match_or!([wrapper_map.get(&hir_id)] Some(x) => x; return (q, p));
let mut new_path = p.clone();
new_path.segments.pop();
new_path.segments.push(mk().path_segment(name));
(q, new_path)
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
struct Abstract {
sig: String,
pat: String,
body: Option<String>,
}
impl Transform for Abstract {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let pat = parse_expr(cx.session(), &self.pat);
let func_src = format!("unsafe fn {} {{\n {}\n}}",
self.sig, self.body.as_ref().unwrap_or(&self.pat));
let func: P<Item> = st.parse_items(cx, &func_src).lone();
st.add_mark(func.id, "new");
let mut value_args = Vec::new();
let mut type_args = Vec::new();
{
let (decl, generics) = expect!([func.node]
ItemKind::Fn(ref decl, _, ref gen, _) => (decl, gen));
for arg in &decl.inputs {
let name = expect!([arg.pat.node] PatKind::Ident(_, ident, _) => ident);
value_args.push(name);
}
for param in &generics.params {
if let GenericParamKind::Type { .. } = param.kind {
type_args.push(param.ident);
}
}
}
let aba = mk().angle_bracketed_args(
type_args.iter().map(|name| mk().ident_ty(name)).collect());
let seg = mk().path_segment_with_args(func.ident, aba);
let call_expr = mk().call_expr(
mk().path_expr(mk().abs_path(vec![seg])),
value_args.iter().map(|name| mk().ident_expr(name)).collect());
let mut init_mcx = MatchCtxt::new(st, cx);
for name in &value_args {
init_mcx.set_type(name.name, BindingType::Expr);
}
for name in &type_args {
init_mcx.set_type(name.name, BindingType::Ty);
}
mut_visit_match_with(init_mcx, pat, krate, |ast, mut mcx| {
for name in &type_args {
if mcx.bindings.get::<_, P<Ty>>(name.name).is_none() {
mcx.bindings.add(name.name, mk().infer_ty());
}
}
*ast = call_expr.clone().subst(st, cx, &mcx.bindings);
});
krate.module.items.push(func);
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub fn register_commands(reg: &mut Registry) {
use super::mk;
reg.register("func_to_method", |_args| mk(ToMethod));
reg.register("fix_unused_unsafe", |_args| mk(FixUnusedUnsafe));
reg.register("sink_unsafe", |_args| mk(SinkUnsafe));
reg.register("wrap_extern", |_args| mk(WrapExtern));
reg.register("wrap_api", |_args| mk(WrapApi));
reg.register("abstract", |args| mk(Abstract {
sig: args[0].clone(),
pat: args[1].clone(),
body: args.get(2).cloned(),
}));
}