use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use regex::Regex;
use rustc::hir::HirId;
use syntax::attr;
use syntax::ast::*;
use syntax::source_map::DUMMY_SP;
use syntax::mut_visit::{self, MutVisitor};
use syntax::ptr::P;
use syntax::symbol::Symbol;
use smallvec::SmallVec;
use c2rust_ast_builder::{mk, Make, IntoSymbol};
use crate::ast_manip::{FlatMapNodes, MutVisit, AstEquiv};
use crate::command::{CommandState, Registry};
use crate::driver::{self, Phase};
use crate::path_edit::fold_resolved_paths;
use crate::transform::Transform;
use crate::RefactorCtxt;
pub struct RenameRegex {
pattern: String,
repl: String,
filter: Option<Symbol>,
}
impl Transform for RenameRegex {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let re = Regex::new(&self.pattern).unwrap();
let mut new_idents = HashMap::new();
FlatMapNodes::visit(krate, |i: P<Item>| {
if let Some(label) = self.filter {
if !st.marked(i.id, label) {
return smallvec![i];
}
}
let name = i.ident.name.as_str();
let new_name = re.replace(&name, &self.repl as &str);
if let Cow::Owned(new_name) = new_name {
new_idents.insert(cx.hir_map().node_to_hir_id(i.id), mk().ident(&new_name));
smallvec![i.map(|i| {
Item {
ident: mk().ident(&new_name),
.. i
}
})]
} else {
smallvec![i]
}
});
fold_resolved_paths(krate, cx, |qself, mut path, def| {
if let Some(hir_id) = cx.def_to_hir_id(def) {
if let Some(new_ident) = new_idents.get(&hir_id) {
path.segments.last_mut().unwrap().ident = new_ident.clone();
}
}
(qself, path)
});
}
}
pub struct RenameUnnamed;
impl Transform for RenameUnnamed {
fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
#[derive(Debug, Default)]
struct Renamer {
items_to_change: HashSet<NodeId>,
new_idents: HashMap<HirId, Ident>,
new_to_old: HashMap<Ident, Ident>,
is_source: bool,
}
let mut renamer: Renamer = Default::default();
let mut counter: usize = 0;
let has_unnamed = |ident: &Ident| { ident.as_str().contains("unnamed") };
let make_name = |counter| { Ident::from_str(&format!("unnamed_{}", counter)) };
FlatMapNodes::visit(krate, |i: P<Item>| {
if attr::contains_name(&i.attrs, "header_src") && !renamer.is_source {
renamer.is_source = true;
}
let is_module = match i.node {
ItemKind::Mod(..) => true,
_ => false,
};
if !has_unnamed(&i.ident) || is_module {
return smallvec![i];
}
let new_name = make_name(counter);
renamer
.new_idents
.insert(cx.hir_map().node_to_hir_id(i.id), new_name);
renamer.new_to_old.insert(new_name, i.ident);
counter += 1;
smallvec![i.map(|i| Item {
ident: new_name,
..i
})]
});
fold_resolved_paths(krate, cx, |qself, mut path, def| {
if let Some(hir_id) = cx.def_to_hir_id(def) {
if let Some(new_ident) = renamer.new_idents.get(&hir_id) {
path.segments.last_mut().unwrap().ident = new_ident.clone();
}
}
(qself, path)
});
if !renamer.is_source {
return;
}
FlatMapNodes::visit(krate, |mut i: P<Item>| {
match i.node {
ItemKind::Mod(ref mut outer_mod) => {
let mut mod_to_old_idents = HashMap::new();
for outer_item in &outer_mod.items {
match outer_item.node {
ItemKind::Mod(ref inner_mod) => {
let mut old_idents: HashMap<Ident, Ident> = HashMap::new();
for inner_item in &inner_mod.items {
if let Some(old_ident) =
renamer.new_to_old.get(&inner_item.ident)
{
old_idents.insert(*old_ident, inner_item.ident);
}
}
mod_to_old_idents.insert(outer_item.ident, old_idents);
}
_ => {}
}
}
for outer_item in &mut outer_mod.items {
match outer_item.node {
ItemKind::Use(ref mut ut) => {
let mut old_idents = HashMap::new();
for segment in &ut.prefix.segments {
if let Some(map) = mod_to_old_idents.get(&segment.ident) {
old_idents = map.clone();
}
}
if !old_idents.is_empty() {
match ut.kind {
UseTreeKind::Nested(ref mut use_trees) => {
for (use_tree, _) in use_trees.iter_mut() {
if let Some(new_ident) =
old_idents.get(&use_tree.ident())
{
let new_path =
mk().path(Path::from_ident(*new_ident));
use_tree.prefix = new_path;
}
}
}
UseTreeKind::Simple(..) => {
for segment in &mut ut.prefix.segments {
if let Some(new_ident) = old_idents.get(&segment.ident)
{
segment.ident = *new_ident;
}
}
}
_ => {}
}
}
}
_ => {}
}
}
}
_ => {}
}
smallvec![i]
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct ReplaceItems;
impl Transform for ReplaceItems {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let mut target_ids = HashSet::new();
let mut repl_id = None;
FlatMapNodes::visit(krate, |i: P<Item>| {
if st.marked(i.id, "repl") {
if repl_id.is_none() {
repl_id = Some(cx.node_def_id(i.id));
} else {
panic!("found multiple `repl` items");
}
}
if st.marked(i.id, "target") {
target_ids.insert(cx.node_def_id(i.id));
smallvec![]
} else {
smallvec![i]
}
});
FlatMapNodes::visit(krate, |i: ImplItem| {
if st.marked(i.id, "repl") {
if repl_id.is_none() {
repl_id = Some(cx.node_def_id(i.id));
} else {
panic!("found multiple `repl` items");
}
}
if st.marked(i.id, "target") {
target_ids.insert(cx.node_def_id(i.id));
smallvec![]
} else {
smallvec![i]
}
});
let repl_id = repl_id.expect("found no `repl` item");
fold_resolved_paths(krate, cx, |qself, path, def| {
match def.opt_def_id() {
Some(def_id) if target_ids.contains(&def_id) =>
(None, cx.def_path(repl_id)),
_ => (qself, path),
}
});
FlatMapNodes::visit(krate, |i: P<Item>| {
let opt_def_id = match i.node {
ItemKind::Impl(_, _, _, _, _, ref ty, _) => cx.try_resolve_ty(ty),
_ => None,
};
if let Some(def_id) = opt_def_id {
if target_ids.contains(&def_id) {
return smallvec![];
}
}
smallvec![i]
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct SetVisibility {
vis_str: String,
}
impl Transform for SetVisibility {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let vis = driver::run_parser(cx.session(), &self.vis_str,
|p| p.parse_visibility(false));
struct SetVisFolder<'a> {
st: &'a CommandState,
vis: Visibility,
in_trait_impl: bool,
}
impl<'a> MutVisitor for SetVisFolder<'a> {
fn flat_map_item(&mut self, mut i: P<Item>) -> SmallVec<[P<Item>; 1]> {
if self.st.marked(i.id, "target") && !i.vis.ast_equiv(&self.vis) {
i = i.map(|mut i| {
i.vis = self.vis.clone();
i
});
}
let was_in_trait_impl = self.in_trait_impl;
self.in_trait_impl = matches!([i.node]
ItemKind::Impl(_, _, _, _, Some(_), _, _));
let r = mut_visit::noop_flat_map_item(i, self);
self.in_trait_impl = was_in_trait_impl;
r
}
fn flat_map_impl_item(&mut self, mut i: ImplItem) -> SmallVec<[ImplItem; 1]> {
if self.in_trait_impl {
return mut_visit::noop_flat_map_impl_item(i, self);
}
if self.st.marked(i.id, "target") {
i.vis = self.vis.clone();
}
mut_visit::noop_flat_map_impl_item(i, self)
}
fn flat_map_foreign_item(&mut self, mut i: ForeignItem) -> SmallVec<[ForeignItem; 1]> {
if self.st.marked(i.id, "target") {
i.vis = self.vis.clone();
}
mut_visit::noop_flat_map_foreign_item(i, self)
}
}
krate.visit(&mut SetVisFolder { st, vis, in_trait_impl: false })
}
}
pub struct SetMutability {
mut_str: String,
}
impl Transform for SetMutability {
fn transform(&self, krate: &mut Crate, st: &CommandState, _cx: &RefactorCtxt) {
let mutbl = <&str as Make<Mutability>>::make(&self.mut_str, &mk());
struct SetMutFolder<'a> {
st: &'a CommandState,
mutbl: Mutability,
}
impl<'a> MutVisitor for SetMutFolder<'a> {
fn flat_map_item(&mut self, mut i: P<Item>) -> SmallVec<[P<Item>; 1]> {
if self.st.marked(i.id, "target") {
i = i.map(|mut i| {
match i.node {
ItemKind::Static(_, ref mut mutbl, _) => *mutbl = self.mutbl,
_ => {},
}
i
});
}
mut_visit::noop_flat_map_item(i, self)
}
fn flat_map_foreign_item(&mut self, mut i: ForeignItem) -> SmallVec<[ForeignItem; 1]> {
if self.st.marked(i.id, "target") {
match i.node {
ForeignItemKind::Static(_, ref mut is_mutbl) =>
*is_mutbl = self.mutbl == Mutability::Mutable,
_ => {},
}
}
mut_visit::noop_flat_map_foreign_item(i, self)
}
}
krate.visit(&mut SetMutFolder { st, mutbl })
}
}
pub struct SetUnsafety {
unsafe_str: String,
}
impl Transform for SetUnsafety {
fn transform(&self, krate: &mut Crate, st: &CommandState, _cx: &RefactorCtxt) {
let unsafety = <&str as Make<Unsafety>>::make(&self.unsafe_str, &mk());
struct SetUnsafetyFolder<'a> {
st: &'a CommandState,
unsafety: Unsafety,
}
impl<'a> MutVisitor for SetUnsafetyFolder<'a> {
fn flat_map_item(&mut self, mut i: P<Item>) -> SmallVec<[P<Item>; 1]> {
if self.st.marked(i.id, "target") {
i = i.map(|mut i| {
match i.node {
ItemKind::Fn(_, ref mut header, _, _) =>
header.unsafety = self.unsafety,
ItemKind::Trait(_, ref mut unsafety, _, _, _) =>
*unsafety = self.unsafety,
ItemKind::Impl(ref mut unsafety, _, _, _, _, _, _) =>
*unsafety = self.unsafety,
_ => {},
}
i
});
}
mut_visit::noop_flat_map_item(i, self)
}
fn flat_map_trait_item(&mut self, mut i: TraitItem) -> SmallVec<[TraitItem; 1]> {
if self.st.marked(i.id, "target") {
match i.node {
TraitItemKind::Method(ref mut sig, _) =>
sig.header.unsafety = self.unsafety,
_ => {},
}
}
mut_visit::noop_flat_map_trait_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(ref mut sig, _) =>
sig.header.unsafety = self.unsafety,
_ => {},
}
}
mut_visit::noop_flat_map_impl_item(i, self)
}
}
krate.visit(&mut SetUnsafetyFolder { st, unsafety })
}
}
pub struct CreateItem {
header: String,
pos: String,
mark: Symbol,
}
impl Transform for CreateItem {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let mark = self.mark;
let inside = match &self.pos as &str {
"inside" => true,
"after" => false,
_ => panic!("expected position to be 'inside' or 'after'"),
};
let items = st.parse_items(cx, &format!("{}", self.header));
for i in &items {
st.add_mark(i.id, "new");
}
struct CreateFolder<'a> {
st: &'a CommandState,
mark: Symbol,
inside: bool,
items: Vec<P<Item>>,
}
impl<'a> CreateFolder<'a> {
fn handle_mod(&mut self, parent_id: NodeId, m: &mut Mod, skip_dummy: bool) {
let mut items = Vec::with_capacity(m.items.len());
let mut insert_inside = self.inside && self.st.marked(parent_id, self.mark);
for i in &m.items {
if insert_inside {
if !skip_dummy || i.span != DUMMY_SP {
items.extend(self.items.iter().cloned());
insert_inside = false;
}
}
let insert = !self.inside && self.st.marked(i.id, self.mark);
items.push(i.clone());
if insert {
items.extend(self.items.iter().cloned());
}
}
if insert_inside {
items.extend(self.items.iter().cloned());
}
m.items = items;
}
}
impl<'a> MutVisitor for CreateFolder<'a> {
fn visit_crate(&mut self, c: &mut Crate) {
self.handle_mod(CRATE_NODE_ID, &mut c.module, true);
mut_visit::noop_visit_mod(&mut c.module, self);
}
fn flat_map_item(&mut self, mut i: P<Item>) -> SmallVec<[P<Item>; 1]> {
let id = i.id;
if let ItemKind::Mod(m) = &mut i.node {
self.handle_mod(id, m, false);
}
mut_visit::noop_flat_map_item(i, self)
}
fn visit_block(&mut self, b: &mut P<Block>) {
let mut stmts = Vec::with_capacity(b.stmts.len());
if self.inside && self.st.marked(b.id, self.mark) {
stmts.extend(self.items.iter().cloned().map(|i| mk().item_stmt(i)));
}
for s in &b.stmts {
let insert = !self.inside && self.st.marked(s.id, self.mark);
stmts.push(s.clone());
if insert {
stmts.extend(self.items.iter().cloned().map(|i| mk().item_stmt(i)));
}
}
b.stmts = stmts;
mut_visit::noop_visit_block(b, self)
}
fn visit_mac(&mut self, mac: &mut Mac) {
mut_visit::noop_visit_mac(mac, self)
}
}
krate.visit(&mut CreateFolder { st, mark, inside, items })
}
}
pub struct DeleteItems;
impl Transform for DeleteItems {
fn transform(&self, krate: &mut Crate, st: &CommandState, _cx: &RefactorCtxt) {
let mark = "target".into_symbol();
struct DeleteFolder<'a> {
st: &'a CommandState,
mark: Symbol,
}
impl<'a> MutVisitor for DeleteFolder<'a> {
fn visit_mod(&mut self, m: &mut Mod) {
m.items.retain(|i| !self.st.marked(i.id, self.mark));
mut_visit::noop_visit_mod(m, self)
}
fn visit_block(&mut self, b: &mut P<Block>) {
b.stmts.retain(|s| match s.node {
StmtKind::Item(ref i) => !self.st.marked(i.id, self.mark),
_ => true,
});
mut_visit::noop_visit_block(b, self)
}
}
krate.visit(&mut DeleteFolder { st, mark })
}
}
pub fn register_commands(reg: &mut Registry) {
use super::mk;
reg.register("rename_items_regex", |args| mk(RenameRegex {
pattern: args[0].clone(),
repl: args[1].clone(),
filter: args.get(2).map(|x| (x as &str).into_symbol()),
}));
reg.register("rename_unnamed", |_args| mk(RenameUnnamed));
reg.register("replace_items", |_args| mk(ReplaceItems));
reg.register("set_visibility", |args| mk(SetVisibility {
vis_str: args[0].clone(),
}));
reg.register("set_mutability", |args| mk(SetMutability {
mut_str: args[0].clone(),
}));
reg.register("set_unsafety", |args| mk(SetUnsafety {
unsafe_str: args[0].clone(),
}));
reg.register("create_item", |args| mk(CreateItem {
header: args[0].clone(),
pos: args[1].clone(),
mark: args.get(2).map(|s| (s as &str).into_symbol())
.unwrap_or_else(|| "target".into_symbol()),
}));
reg.register("delete_items", |_args| mk(DeleteItems));
}