1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use rustc::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
use syntax::ast::{Crate, Expr, ExprKind, Mutability, UnOp};
use syntax::ptr::P;

use c2rust_ast_builder::mk;
use crate::ast_manip::MutVisitNodes;
use crate::command::{CommandState, Registry};
use crate::driver::Phase;
use crate::transform::Transform;
use crate::RefactorCtxt;

/// Transformation that makes all autorefs and autoderefs explicit.
struct CanonicalizeRefs;

impl Transform for CanonicalizeRefs {
    fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
        MutVisitNodes::visit(krate, |expr: &mut P<Expr>| {
            let hir_expr = cx.hir_map().expect_expr(expr.id);
            let parent = cx.hir_map().get_parent_did(expr.id);
            let tables = cx.ty_ctxt().typeck_tables_of(parent);
            for adjustment in tables.expr_adjustments(hir_expr) {
                match adjustment.kind {
                    Adjust::Deref(_) => {
                        *expr = mk().unary_expr(UnOp::Deref, expr.clone());
                    }
                    Adjust::Borrow(AutoBorrow::Ref(_, ref mutability)) => {
                        let mutability = match mutability {
                            AutoBorrowMutability::Mutable{..} => Mutability::Mutable,
                            AutoBorrowMutability::Immutable => Mutability::Immutable,
                        };
                        *expr = mk().set_mutbl(mutability).addr_of_expr(expr.clone());
                    }
                    _ => {},
                }
            }
        });
    }

    fn min_phase(&self) -> Phase {
        Phase::Phase3
    }
}


/// Transformation that removes unnecessary refs and derefs.
struct RemoveUnnecessaryRefs;

impl Transform for RemoveUnnecessaryRefs {
    fn transform(&self, krate: &mut Crate, _st: &CommandState, _cx: &RefactorCtxt) {
        MutVisitNodes::visit(krate, |expr: &mut P<Expr>| {
            match &mut expr.node {
                ExprKind::MethodCall(_path, args) => {
                    let (receiver, rest) = args.split_first_mut().unwrap();
                    remove_reborrow(receiver);
                    remove_ref(receiver);
                    remove_all_derefs(receiver);
                    for arg in rest {
                        remove_reborrow(arg);
                    }
                }
                ExprKind::Call(_callee, args) => {
                    for arg in args.iter_mut() {
                        remove_reborrow(arg);
                    }
                }
                _ => {}
            }
        });
    }

    fn min_phase(&self) -> Phase {
        Phase::Phase3
    }
}

fn remove_ref(expr: &mut P<Expr>) {
    match &expr.node {
        ExprKind::AddrOf(_, inner) => *expr = inner.clone(),
        _ => {}
    }
}

fn remove_all_derefs(expr: &mut P<Expr>) {
    match &expr.node {
        ExprKind::Unary(UnOp::Deref, inner) => {
            *expr = inner.clone();
            remove_all_derefs(expr);
        }
        _ => {}
    }
}

fn remove_reborrow(expr: &mut P<Expr>) {
    if let ExprKind::AddrOf(_, ref subexpr) = expr.node {
        if let ExprKind::Unary(UnOp::Deref, ref subexpr) = subexpr.node {
            *expr = subexpr.clone();
            remove_reborrow(expr);
        }
    }
}

pub fn register_commands(reg: &mut Registry) {
    use super::mk;

    reg.register("canonicalize_refs", |_args| mk(CanonicalizeRefs));

    reg.register("remove_unnecessary_refs", |_args| mk(RemoveUnnecessaryRefs));
}