Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Latest commit

 

History

History
141 lines (120 loc) · 3.69 KB

COOKBOOK.md

File metadata and controls

141 lines (120 loc) · 3.69 KB

Rerast cookbook

Here we've got some examples of things you can do with rerast. If you've got any more examples, please feel free to send pull requests.

Replace try!(something) with something?

fn rule1<T,E,X: From<E>>(r: Result<T,E>) -> Result<T,X> {
    replace!(r#try!(r) => r?);
    unreachable!()
}

This will change:

pub fn get_file_contents(filename: &std::path::Path) -> std::io::Result<String> {
    let mut result = String::new();
    use std::io::Read;
    try!(try!(std::fs::File::open(filename)).read_to_string(&mut result));
    Ok(result)
}

Into this:

pub fn get_file_contents(filename: &std::path::Path) -> std::io::Result<String> {
    let mut result = String::new();
    use std::io::Read;
    std::fs::File::open(filename)?.read_to_string(&mut result)?;
    Ok(result)
}

This rule also shows how to handle rules that have return statements in them. i.e. specify a return type for your rule function. unreachable!() can then be used to avoid having to construct an actual value.

Use accessors instead of direct field access

fn r(mut p: Point, x: i32, y: i32) {
    replace!(Point{ x: x, y: y } => Point::new(x, y));
    replace!(p.x = x => p.set_x(x));
    replace!(&mut p.x => p.get_mut_x());
    replace!(p.x => p.get_x());
}

This will change:

fn f1(point: Point, point_ref: &Point, mut_point_ref: &mut Point) {
    let p2 = Point { x: 1, y: 2 };
    process_i32(point.x);
    mut_point_ref.x = 1;
    let x = &mut mut_point_ref.x;
    *x = 42;
    process_i32(point_ref.x);
}

Into:

fn f1(point: Point, point_ref: &Point, mut_point_ref: &mut Point) {
    let p2 = Point::new(1, 2);
    process_i32(point.get_x());
    mut_point_ref.set_x(1);
    let x = mut_point_ref.get_mut_x();
    *x = 42;
    process_i32(point_ref.get_x());
}

Remove the argument from a function

This is a real world example from the Cargo project (issue, PR).

use cargotest::support::{ project, project_foo };

fn rule1(a: &'static str) {
   replace!(project("foo") => project_foo());
   replace!(project(a) => project_foo().at(a));
}

Changes:

let p = project("foo")
    .file("Cargo.toml", &basic_bin_manifest("foo"))
    .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
    .build();
let _p2 = project("bar")
    .file("Cargo.toml", &basic_bin_manifest("bar"))
    .file("src/bar.rs", &main_file(r#""i am bar""#, &[]))
    .build();

Into:

let p = project_foo()
    .file("Cargo.toml", &basic_bin_manifest("foo"))
    .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
    .build();
let _p2 = project_foo().at("bar")
    .file("Cargo.toml", &basic_bin_manifest("bar"))
    .file("src/bar.rs", &main_file(r#""i am bar""#, &[]))
    .build();

When run like this:

cargo +nightly rerast --rules_file=rewrite.rs --force --targets tests --file tests/testsuite/main.rs

Afterwhich a simple search and replace can rename project_foo back to project and the argument can be dropped.

Replace await! macro with .await

Since await can only be used from within an async function, you'll need to delcare your rule function async. This cannot be done from the command line, so you'll need to create a rule file with the following contents:

async fn rule1<T: std::future::Future>(r: T) {
  replace!(await!(r) => r.await)
}

Replace yield

Since yield can only be used from within a generator, you'll need to delcare your replace rule inside a generator. For example:

fn r1(x: i32) {
    let _ = || {
        replace!(yield x => yield x + 1);
    };
}