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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
//! Transformation passes used for testing parts of the system.

use std::collections::{HashSet, HashMap};
use std::str::FromStr;
use syntax::ast::*;
use syntax::ptr::P;
use rustc::hir;
use rustc::ty::{self, TyCtxt, ParamEnv};
use rustc::ty::subst::InternalSubsts;

use c2rust_ast_builder::mk;
use crate::ast_manip::{visit_nodes};
use crate::ast_manip::fn_edit::mut_visit_fns;
use crate::command::{RefactorState, CommandState, Command, Registry, TypeckLoopResult};
use crate::driver::{Phase};
use crate::matcher::{replace_expr, replace_stmts};
use crate::transform::Transform;
use crate::RefactorCtxt;


/// # `test_one_plus_one` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_one_plus_one`
/// 
/// Replace the expression `2` with `1 + 1` everywhere it appears.
pub struct OnePlusOne;

impl Transform for OnePlusOne {
    fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
        let krate = replace_expr(st, cx, krate, "2", "1 + 1");
        krate
    }
}


/// # `test_f_plus_one` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_f_plus_one`
/// 
/// Replace the expression `f(__x)` with `__x + 1` everywhere it appears.
pub struct FPlusOne;

impl Transform for FPlusOne {
    fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
        let krate = replace_expr(st, cx, krate, "f(__x)", "__x + 1");
        krate
    }
}


/// # `test_replace_stmts` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_replace_stmts OLD NEW`
/// 
/// Replace statement(s) `OLD` with `NEW` everywhere it appears.
pub struct ReplaceStmts(pub String, pub String);

impl Transform for ReplaceStmts {
    fn transform(&self, krate: &mut Crate, st: &CommandState, cx: &RefactorCtxt) {
        let krate = replace_stmts(st, cx, krate, &self.0, &self.1);
        krate
    }
}


/// # `test_insert_remove_args` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_insert_remove_args INS REM`
/// 
/// In each function marked `target`, insert new arguments at each index listed in
/// `INS` (a comma-separated list of integers), then delete the arguments whose
/// original indices are listed in `REM`.
/// 
/// This is used for testing sequence rewriting of `fn` argument lists.
pub struct InsertRemoveArgs {
    insert_idxs: HashMap<usize, usize>,
    remove_idxs: HashSet<usize>,
}

impl Transform for InsertRemoveArgs {
    fn transform(&self, krate: &mut Crate, st: &CommandState, _cx: &RefactorCtxt) {
        mut_visit_fns(krate, |fl| {
            if !st.marked(fl.id, "target") {
                return;
            }

            let mut counter = 0;
            let mut mk_arg = || {
                let arg = mk().arg(mk().tuple_ty::<P<Ty>>(vec![]),
                                   mk().ident_pat(&format!("new_arg{}", counter)));
                counter += 1;
                arg
            };

            let mut new_args = Vec::new();
            let old_arg_count = fl.decl.inputs.len();
            for (i, arg) in fl.decl.inputs.iter().enumerate() {
                for _ in 0 .. self.insert_idxs.get(&i).cloned().unwrap_or(0) {
                    new_args.push(mk_arg());
                }

                if !self.remove_idxs.contains(&i) {
                    new_args.push(arg.clone());
                }
            }

            for _ in 0 .. self.insert_idxs.get(&old_arg_count).cloned().unwrap_or(0) {
                new_args.push(mk_arg());
            }

            fl.decl.inputs = new_args;
        });
    }
}


/// # `test_typeck_loop` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_typeck_loop`
/// 
/// Runs a no-op typechecking loop for three iterations.  Used to test the typechecking loop and
/// AST re-analysis code.
pub struct TestTypeckLoop;

impl Command for TestTypeckLoop {
    fn run(&mut self, state: &mut RefactorState) {
        let mut i = 3;
        state.run_typeck_loop(|_krate, _st, _cx| {
            i -= 1;
            info!("ran typeck loop iteration {}", i);
            if i == 0 {
                TypeckLoopResult::Finished
            } else {
                TypeckLoopResult::Iterate
            }
        }).unwrap();
    }
}


/// # `test_debug_callees` Command
/// 
/// Test command - not intended for general use.
/// 
/// Usage: `test_debug_callees`
/// 
/// Inspect the details of each Call expression.  Used to debug
/// `RefactorCtxt::opt_callee_info`.
pub struct TestDebugCallees;

impl Transform for TestDebugCallees {
    fn transform(&self, krate: &mut Crate, _st: &CommandState, cx: &RefactorCtxt) {
        visit_nodes(krate, |e: &Expr| {
            let tcx = cx.ty_ctxt();
            let hir_map = cx.hir_map();

            let parent = hir_map.get_parent(e.id);
            let parent_body = match_or!([hir_map.maybe_body_owned_by(parent)]
                                        Some(x) => x; return);
            let tables = tcx.body_tables(parent_body);
            let tdds = tables.type_dependent_defs();

            fn maybe_info<T: ::std::fmt::Debug>(desc: &str, x: Option<T>) {
                if let Some(x) = x {
                    info!("    {}: {:?}", desc, x);
                }
            }

            fn describe_ty<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                         desc: &str,
                                         ty: ty::Ty<'tcx>,
                                         substs: Option<&'tcx InternalSubsts<'tcx>>) {
                info!("    {}: {:?}", desc, ty);
                if let Some(substs) = substs {
                    info!("      subst: {:?}",
                          tcx.subst_and_normalize_erasing_regions(
                              substs, ParamEnv::empty(), &ty));
                }
                if ty.is_fn() {
                    let sig = ty.fn_sig(tcx);
                    info!("      fn sig: {:?}", sig);
                    info!("      input tys: {:?}", sig.inputs());
                    info!("      input tys (skip): {:?}", sig.skip_binder().inputs());
                    info!("      anonymized: {:?}", tcx.anonymize_late_bound_regions(&sig));
                    info!("      erased: {:?}", tcx.erase_late_bound_regions(&sig));
                    if let Some(substs) = substs {
                        let sig2 = tcx.subst_and_normalize_erasing_regions(
                            substs, ParamEnv::empty(),
                            &tcx.erase_late_bound_regions(&sig));
                        info!("      sig + erase + subst: {:?}", sig2);
                        info!("      input tys: {:?}", sig2.inputs());
                    }
                }
            };

            let describe = |e: &Expr| {
                info!("    expr: {:?}", e);
                let hir_id = hir_map.node_to_hir_id(e.id);
                info!("    hir id: {:?}", hir_id);

                if let Some(hir::Node::Expr(hir_expr)) = hir_map.find(e.id) {
                    info!("    hir expr: {:?}", hir_expr);
                    maybe_info("ty", tables.expr_ty_opt(hir_expr));
                    maybe_info("adj ty", tables.expr_ty_adjusted_opt(hir_expr));
                }

                let opt_substs = tables.node_substs_opt(hir_id);
                maybe_info("substs", opt_substs);

                if let Some(did) = cx.try_resolve_expr(e) {
                    info!("    resolution: {:?}", did);
                    describe_ty(tcx, "resolved ty", tcx.type_of(did), opt_substs);
                }

                if let Some(tdd) = tdds.get(hir_id) {
                    info!("    tdd: {:?}", tdd);
                    if let Some(did) = tdd.opt_def_id() {
                        info!("    tdd id: {:?}", did);
                        describe_ty(tcx, "tdd ty", tcx.type_of(did), opt_substs);
                    }
                }
            };

            match e.node {
                ExprKind::Call(ref func, _) => {
                    info!("at plain call {:?}", e);
                    info!("  call info:");
                    describe(e);
                    info!("  func info:");
                    describe(func);
                },

                ExprKind::MethodCall(_, _) => {
                    info!("at method call {:?}", e);
                    info!("  call info:");
                    describe(e);
                },

                ExprKind::Binary(_, _, _) => {
                    info!("at binary op {:?}", e);
                    describe(e);
                },

                _ => {},
            }
        });
    }

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


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

    reg.register("test_one_plus_one", |_args| mk(OnePlusOne));
    reg.register("test_f_plus_one", |_args| mk(FPlusOne));
    reg.register("test_replace_stmts", |args| mk(
            ReplaceStmts(args[0].clone(), args[1].clone())));

    reg.register("test_insert_remove_args", |args| {
        let mut insert_idxs = HashMap::new();
        let mut remove_idxs = HashSet::new();

        for part in args[0].split(",") {
            if part == "" {
                continue;
            }
            let idx = usize::from_str(part).unwrap();
            *insert_idxs.entry(idx).or_insert(0) += 1;
        }

        for part in args[1].split(",") {
            if part == "" {
                continue;
            }
            let idx = usize::from_str(part).unwrap();
            remove_idxs.insert(idx);
        }

        mk(InsertRemoveArgs { insert_idxs, remove_idxs })
    });

    reg.register("test_typeck_loop", |_| Box::new(TestTypeckLoop));

    reg.register("test_debug_callees", |_args| mk(TestDebugCallees));
}