use rustc::hir;
use rustc::hir::def::Def;
use rustc::ty::{self, ParamEnv, TyCtxt};
use smallvec::SmallVec;
use syntax::ast::*;
use syntax::mut_visit::{self, MutVisitor};
use syntax::ptr::P;
use crate::ast_manip::MutVisit;
use crate::RefactorCtxt;
fn types_approx_equal<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
ty1: ty::Ty<'tcx>,
ty2: ty::Ty<'tcx>,
) -> bool {
let ty1 = tcx.normalize_erasing_regions(ParamEnv::empty(), ty1);
let ty2 = tcx.normalize_erasing_regions(ParamEnv::empty(), ty2);
ty1 == ty2
}
pub trait IlltypedFolder<'tcx> {
#[allow(unused)]
fn fix_expr(&mut self, e: &mut P<Expr>, actual: ty::Ty<'tcx>, expected: ty::Ty<'tcx>) {}
fn fix_expr_cast(&mut self, e: &mut P<Expr>, actual: ty::Ty<'tcx>, target: ty::Ty<'tcx>) {
self.fix_expr(e, actual, target)
}
fn fix_expr_parent(&mut self, _e: &mut P<Expr>) {}
}
impl<'a, 'tcx, F: IlltypedFolder<'tcx>> IlltypedFolder<'tcx> for &'a mut F {
fn fix_expr(&mut self, e: &mut P<Expr>, actual: ty::Ty<'tcx>, expected: ty::Ty<'tcx>) {
<F as IlltypedFolder>::fix_expr(self, e, actual, expected)
}
fn fix_expr_cast(&mut self, e: &mut P<Expr>, actual: ty::Ty<'tcx>, target: ty::Ty<'tcx>) {
<F as IlltypedFolder>::fix_expr_cast(self, e, actual, target)
}
fn fix_expr_parent(&mut self, e: &mut P<Expr>) {
<F as IlltypedFolder>::fix_expr_parent(self, e)
}
}
struct FoldIlltyped<'a, 'tcx, F> {
cx: &'a RefactorCtxt<'a, 'tcx>,
inner: F,
}
impl<'a, 'tcx, F: IlltypedFolder<'tcx>> FoldIlltyped<'a, 'tcx, F> {
fn ensure(&mut self, expr: &mut P<Expr>, expected_ty: ty::Ty<'tcx>) -> bool {
if let Some(actual_ty) = self.cx.opt_adjusted_node_type(expr.id) {
if !types_approx_equal(self.cx.ty_ctxt(), actual_ty, expected_ty) {
self.inner.fix_expr(expr, actual_ty, expected_ty);
return true;
}
}
false
}
#[allow(dead_code)]
fn ensure_cast(&mut self, sub_e: &mut P<Expr>, target_ty: ty::Ty<'tcx>) -> bool {
if let Some(actual_ty) = self.cx.opt_adjusted_node_type(sub_e.id) {
self.inner.fix_expr_cast(sub_e, actual_ty, target_ty);
return true;
}
false
}
}
impl<'a, 'tcx, F: IlltypedFolder<'tcx>> MutVisitor for FoldIlltyped<'a, 'tcx, F> {
fn visit_expr(&mut self, e: &mut P<Expr>) {
let mut illtyped = false;
mut_visit::noop_visit_expr(e, self);
let ty = match self.cx.opt_node_type(e.id) {
Some(x) => x,
None => return,
};
if let ty::TyKind::Error = ty.sty {
return;
}
let opt_fn_sig = self.cx.opt_callee_fn_sig(&e);
let tcx = self.cx.ty_ctxt();
let id = e.id;
match &mut e.node {
ExprKind::Box(content) => {
illtyped |= self.ensure(content, ty.boxed_ty());
}
ExprKind::ObsoleteInPlace(..) => {}
ExprKind::Array(elems) => {
let expected_elem_ty = ty.builtin_index().unwrap();
for e in elems {
illtyped |= self.ensure(e, expected_elem_ty);
}
}
ExprKind::Repeat(elem, _count) => {
let expected_elem_ty = ty.builtin_index().unwrap();
illtyped |= self.ensure(elem, expected_elem_ty);
}
ExprKind::Tup(elems) => {
let elem_tys = expect!([ty.sty] ty::TyKind::Tuple(elem_tys) => elem_tys);
for (elem, elem_ty) in elems.iter_mut().zip(elem_tys) {
illtyped |= self.ensure(elem, elem_ty);
}
}
ExprKind::Call(_callee, args) => {
if let Some(fn_sig) = opt_fn_sig {
for (i, arg) in args.iter_mut().enumerate() {
if !fn_sig.c_variadic || i < fn_sig.inputs().len() - 1 {
if let Some(&ty) = fn_sig.inputs().get(i) {
illtyped |= self.ensure(arg, ty);
}
}
}
}
}
ExprKind::MethodCall(_seg, args) => {
if let Some(fn_sig) = opt_fn_sig {
for (i, arg) in args.iter_mut().enumerate() {
if let Some(&ty) = fn_sig.inputs().get(i) {
illtyped |= self.ensure(arg, ty);
}
}
}
}
ExprKind::Binary(binop, lhs, rhs) => {
use syntax::ast::BinOpKind::*;
match binop.node {
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr => {
illtyped |= self.ensure(lhs, ty);
illtyped |= self.ensure(rhs, ty);
}
Eq | Lt | Le | Ne | Ge | Gt => {
if let Some(lhs_ty) = self.cx.opt_node_type(lhs.id) {
illtyped |= self.ensure(rhs, lhs_ty);
} else if let Some(rhs_ty) = self.cx.opt_node_type(rhs.id) {
illtyped |= self.ensure(lhs, rhs_ty);
}
}
Shl | Shr => {
illtyped |= self.ensure(lhs, ty);
}
And | Or => {
illtyped |= self.ensure(lhs, ty);
illtyped |= self.ensure(rhs, ty);
}
}
}
ExprKind::Unary(_binop, _ohs) => {
}
ExprKind::Lit(_l) => {}
ExprKind::Cast(_sub_e, _target) => {
}
ExprKind::AddrOf(_m, _ohs) => {}
ExprKind::If(cond, _tr, _fl) => {
illtyped |= self.ensure(cond, tcx.mk_bool());
}
ExprKind::IfLet(pats, expr, _tr, _fl) => {
if let Some(pat_ty) = self.cx.opt_node_type(pats[0].id) {
illtyped |= self.ensure(expr, pat_ty);
}
}
ExprKind::While(cond, _body, _opt_label) => {
illtyped |= self.ensure(cond, tcx.mk_bool());
}
ExprKind::WhileLet(pats, expr, _body, _opt_label) => {
if let Some(pat_ty) = self.cx.opt_node_type(pats[0].id) {
illtyped |= self.ensure(expr, pat_ty);
}
}
ExprKind::Match(expr, arms) => {
if let Some(pat_ty) = arms
.get(0)
.and_then(|arm| self.cx.opt_node_type(arm.pats[0].id))
{
illtyped |= self.ensure(expr, pat_ty);
}
}
ExprKind::Block(_blk, _opt_label) => {
}
ExprKind::Assign(el, er) => {
let lhs_ty = self.cx.node_type(el.id);
illtyped |= self.ensure(er, lhs_ty);
}
ExprKind::AssignOp(_op, el, er) => {
let lhs_ty = self.cx.node_type(el.id);
illtyped |= self.ensure(er, lhs_ty);
}
ExprKind::Index(_el, er) => {
illtyped |= self.ensure(er, tcx.mk_mach_uint(UintTy::Usize));
}
ExprKind::Range(_e1, _e2, _lim) => {
}
ExprKind::Struct(_path, fields, maybe_expr) => {
handle_struct(self.cx, id, ty, fields, maybe_expr, |e, ty| {
illtyped |= self.ensure(e, ty)
});
}
_ => {}
};
if illtyped {
self.inner.fix_expr_parent(e)
}
}
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
let mut items = mut_visit::noop_flat_map_item(i, self);
for i in items.iter_mut() {
let id = i.id;
match &mut i.node {
ItemKind::Static(_ty, _mutbl, expr) => {
let did = self.cx.node_def_id(id);
let expected_ty = self.cx.ty_ctxt().type_of(did);
info!("STATIC: expected ty {:?}, expr {:?}", expected_ty, expr);
let tcx = self.cx.ty_ctxt();
let node_id = tcx.hir().as_local_node_id(did).unwrap();
match tcx.hir().get(node_id) {
hir::Node::Item(item) => match item.node {
hir::ItemKind::Static(ref t, ..) => info!(" - ty hir = {:?}", t),
_ => {}
},
_ => {}
}
self.ensure(expr, expected_ty);
}
ItemKind::Const(_ty, expr) => {
let did = self.cx.node_def_id(id);
let expected_ty = self.cx.ty_ctxt().type_of(did);
self.ensure(expr, expected_ty);
}
_ => {}
}
}
items
}
}
fn handle_struct<'tcx, F>(
cx: &RefactorCtxt<'_, 'tcx>,
expr_id: NodeId,
ty: ty::Ty<'tcx>,
fields: &mut Vec<Field>,
maybe_expr: &mut Option<P<Expr>>,
mut ensure: F,
) where
F: FnMut(&mut P<Expr>, ty::Ty<'tcx>),
{
let (adt_def, substs) = match ty.sty {
ty::TyKind::Adt(a, s) => (a, s),
_ => return,
};
let variant_hir_def = match_or!([resolve_struct_path(cx, expr_id)] Some(x) => x;
return);
let vdef = adt_def.variant_of_def(variant_hir_def);
mut_visit::visit_vec(fields, |f| {
let idx = match_or!([cx.ty_ctxt().find_field_index(f.ident, vdef)] Some(x) => x; return);
let fdef = &vdef.fields[idx];
let field_ty = fdef.ty(cx.ty_ctxt(), substs);
ensure(&mut f.expr, field_ty);
});
mut_visit::visit_opt(maybe_expr, |e| ensure(e, ty));
}
fn resolve_struct_path(cx: &RefactorCtxt, id: NodeId) -> Option<Def> {
let node = match_or!([cx.hir_map().find(id)] Some(x) => x; return None);
let expr = match_or!([node] hir::Node::Expr(e) => e; return None);
let qpath: &hir::QPath =
match_or!([expr.node] hir::ExprKind::Struct(ref q, ..) => q; return None);
let path = match_or!([qpath] hir::QPath::Resolved(_, ref path) => path; return None);
Some(path.def)
}
pub fn fold_illtyped<'tcx, F, T>(cx: &RefactorCtxt<'_, 'tcx>, x: &mut T, f: F)
where
F: IlltypedFolder<'tcx>,
T: MutVisit,
{
let mut f2 = FoldIlltyped { cx, inner: f };
x.visit(&mut f2)
}