use std::collections::HashMap;
use rustc::hir::def_id::DefId;
use syntax::ast::*;
use syntax::attr;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::sym;
use smallvec::smallvec;
use crate::ast_manip::{FlatMapNodes, MutVisitNodes, visit_nodes};
use crate::ast_manip::fn_edit::{visit_fns, FnKind};
use crate::command::{CommandState, Registry};
use crate::driver::{Phase};
use crate::path_edit::fold_resolved_paths;
use crate::transform::Transform;
use crate::RefactorCtxt;
pub struct LinkFuncs;
impl Transform for LinkFuncs {
fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
let mut symbol_to_def = HashMap::new();
let mut extern_def_to_symbol = HashMap::new();
visit_fns(krate, |fl| {
let def_id = cx.node_def_id(fl.id);
if fl.kind != FnKind::Foreign {
if let Some(name) = attr::first_attr_value_str_by_name(&fl.attrs, sym::export_name) {
symbol_to_def.insert(name, def_id);
} else if attr::contains_name(&fl.attrs, sym::no_mangle) {
symbol_to_def.insert(fl.ident.name, def_id);
}
} else {
extern_def_to_symbol.insert(def_id, fl.ident.name);
}
});
fold_resolved_paths(krate, cx, |qself, path, def| {
if let Some(def_id) = def[0].opt_def_id() {
if let Some(&symbol) = extern_def_to_symbol.get(&def_id) {
if let Some(&real_def_id) = symbol_to_def.get(&symbol) {
return (None, cx.def_path(real_def_id));
}
}
}
(qself, path)
});
MutVisitNodes::visit(krate, |fm: &mut ForeignMod| {
fm.items.retain(|i| {
let def_id = cx.node_def_id(i.id);
if let Some(&symbol) = extern_def_to_symbol.get(&def_id) {
if let Some(&_real_def_id) = symbol_to_def.get(&symbol) {
return false;
}
}
true
});
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct LinkIncompleteTypes;
impl Transform for LinkIncompleteTypes {
fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
let mut name_to_complete = HashMap::new();
let mut incomplete_to_name = HashMap::new();
visit_nodes(krate, |i: &Item| {
let complete = match i.kind {
ItemKind::Struct(..) => true,
ItemKind::Union(..) => true,
ItemKind::Enum(..) => true,
ItemKind::TyAlias(..) => true,
_ => false,
};
if complete {
let def_id = cx.node_def_id(i.id);
name_to_complete.entry(i.ident.name).or_insert_with(Vec::new).push(def_id);
}
});
visit_nodes(krate, |i: &ForeignItem| {
let incomplete = match i.kind {
ForeignItemKind::Ty => true,
_ => false,
};
if incomplete {
let def_id = cx.node_def_id(i.id);
incomplete_to_name.insert(def_id, i.ident.name);
}
});
fold_resolved_paths(krate, cx, |qself, path, def| {
if let Some(&name) = def[0].opt_def_id().as_ref().and_then(|x| incomplete_to_name.get(x)) {
if let Some(complete_def_ids) = name_to_complete.get(&name) {
return (None, cx.def_path(complete_def_ids[0]));
}
}
(qself, path)
})
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub struct CanonicalizeStructs;
impl Transform for CanonicalizeStructs {
fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
let mut canon_ids: HashMap<Symbol, DefId> = HashMap::new();
visit_nodes(krate, |i: &Item| {
if st.marked(i.id, "target") {
canon_ids.insert(i.ident.name, cx.node_def_id(i.id));
}
});
let mut removed_id_map = HashMap::new();
FlatMapNodes::visit(krate, |i: P<Item>| {
let should_remove = match i.kind {
ItemKind::Struct(..) => {
if let Some(&canon_def_id) = canon_ids.get(&i.ident.name) {
let def_id = cx.node_def_id(i.id);
if def_id != canon_def_id {
removed_id_map.insert(cx.node_def_id(i.id), canon_def_id);
true
} else {
false
}
} else {
false
}
},
_ => false,
};
if should_remove {
smallvec![]
} else {
smallvec![i]
}
});
FlatMapNodes::visit(krate, |i: P<Item>| {
let should_remove = match i.kind {
ItemKind::Impl(_, _, _, _, _, ref ty, _) => {
if let Some(ty_def_id) = cx.try_resolve_ty(ty) {
removed_id_map.contains_key(&ty_def_id)
} else {
false
}
}
_ => false,
};
if should_remove {
smallvec![]
} else {
smallvec![i]
}
});
fold_resolved_paths(krate, cx, |qself, path, def| {
if let Some(&canon_def_id) = def[0].opt_def_id().as_ref()
.and_then(|x| removed_id_map.get(&x)) {
(None, cx.def_path(canon_def_id))
} else {
(qself, path)
}
});
}
fn min_phase(&self) -> Phase {
Phase::Phase3
}
}
pub fn register_commands(reg: &mut Registry) {
use super::mk;
reg.register("link_funcs", |_args| mk(LinkFuncs));
reg.register("link_incomplete_types", |_args| mk(LinkIncompleteTypes));
reg.register("canonicalize_structs", |_args| mk(CanonicalizeStructs));
}