Rustfmt: Repeatedly running `cargo fmt` does not reach a fixpoint

Created on 28 Feb 2017  路  9Comments  路  Source: rust-lang/rustfmt

Input (reduced via creduce, so no longer valid Rust code, but it was at one point in time):

impl a {
    fn b() {
        {
            {
                self.filter(|base| {
                        ctx.resolve_type(base.ty).has_vtable(ctx)
                    })
                    .c > e = d
            }
        }
    }
}

With this rustfmt.toml:

max_width = 80
format_strings = false
fn_brace_style = "SameLineWhere"
item_brace_style = "SameLineWhere"
struct_lit_multiline_style = "ForceMulti"
where_trailing_comma = true
reorder_imports = true
reorder_imported_names = true
normalize_comments = false
write_mode = "Overwrite"

Steps to reproduce:

$ cargo new tempcrate
$ cp $TESTCASE tempcrate/src/lib.rs
$ cp $RUSTFMT_TOML tempcrate/rustfmt.toml
$ cd tempcrate
$ git add .
$ git commit -m "Initial commit"
$ cargo fmt
$ git commit -am "Running cargo fmt once reformatted"
$ cargo fmt
$ git commit -am "Running cargo fmt a second time ALSO reformatted"
$ git diff HEAD^^ # No diff! Reformatted back to the initial commit! Infinite loop, no fixpoint of formatting

cc @nrc

only-with-option

Most helpful comment

Also, it seems like it would be a good idea for the extant tests (or test runner) to try reformatting again after formatting the initial time, and assert that there is no diff.

All 9 comments

Let me try and re-reduce while keeping the reduced file valid Rust

Here is a valid Rust file that exhibits the same behavior:

struct Base {
    ty: Ty
}

#[derive(Copy, Clone)]
struct Ty;

impl Ty {
    fn has_vtable(&self, ctx: &CodeGenContext) -> bool {
        unimplemented!()
    }
}

struct BaseMembers;

impl BaseMembers {
    fn base_members(&self) -> Vec<Base> {
        unimplemented!()
    }
}

struct CodeGenContext;

impl CodeGenContext {
    fn resolve_type(&self, t: Ty) -> Ty {
        t
    }
}

trait CodeGenerator {
    fn codegen(&self, ctx: &CodeGenContext);
}

impl CodeGenerator for BaseMembers {
    fn codegen(&self, ctx: &CodeGenContext) {
        if false {
            if false {
                let some_variable = self.base_members()
                    .iter()
                    .filter(|base| {
                        ctx.resolve_type(base.ty).has_vtable(ctx)
                    })
                    .count() > 1;
            }
        }
    }
}

Also, it seems like it would be a good idea for the extant tests (or test runner) to try reformatting again after formatting the initial time, and assert that there is no diff.

@fitzgen Also seems like a cool (potential) use of a fuzzer (cc @frewsxcv @Manishearth @nagisa).

@eddyb this is actually derived from rust-bindgen, not generated by a fuzzer. I figured a multiple thousands of lines test case wouldn't be appreciated, hence creduce to take the large test case and make it into a smaller test case :)

(potential)

Ah ok -- yes, it would be great to start fuzzing rustfmt :)

At least some part of the config seems necessary, I can't repro with the default settings.

I suspect it is a combination of the column width and maybe some of the others. I did not creduce the rustfmt.toml :-P

Was this page helpful?
0 / 5 - 0 ratings