Rustfmt-ing Rust

Rustfmt is a tool for formatting Rust code. It has seen some rapid and impressive development over the last few months, thanks to some awesome contributions from @marcusklaas, @cassiersg, and several others. It is far from finished, but it is a powerful and useful tool. I would like Rustfmt to be a standard part of every Rustacean's toolkit. In particular, I would like Rustfmt to be used on every check-in of the Rust repo (and other large projects). For this to be possible, running Rustfmt on Rust must work without crashes, without generating poor formatting, and the whole repo must be pre-formatted so that future changes are not polluted with tonnes of formatting churn.

To work towards this, I've been running Rustfmt on some crates and modules. I would love to have some help doing this! It should be fairly easy to do with no experience with Rust or Rustfmt necessary (and you certainly don't need to know about compiler or library implementation). Hopefully you'll learn a fair bit about the Rust source code and Rustfmt in the process. This blog post is all about how to help.

For this blog post, I'll assume you don't know much about the world of Rust and go over the background etc. in a little detail. Feel free to ping me on irc or GitHub (nrc in both places) if you need any help.

Background

The Rust repo

Can be found at https://github.com/rust-lang/rust. It contains the source code for the Rust compiler and the standard library. When people talk about contributing to the Rust project, they often mean this repo. There are also other important repos in the rust-lang org.

Rustfmt

Rustfmt is a tool for formatting Rust code. Rustfmt is not finished by a long shot, and there are plenty of bugs, as well as some code it doesn't even try to reformat yet. You can find the rustfmt source at https://github.com/nrc/rustfmt.

The problem

We would like to be able to run Rustfmt on the Rust repo. Ideally, we'd like to run it as part of the test suite to make sure it is properly styled. However, there are two problems with this: having not run Rustfmt on it before, there will be a lot of changes the first time it is run; and there are lots of bugs in Rustfmt which we haven't found and prevent us using it on a project the size of Rust.

The solution

Pick a module or sub-module (best to start small, I wouldn't try whole crates), run Rustfmt on it and inspect the result, if there are problems (or Rustfmt crashes while running), file an issue against Rustfmt. If it succeeds (possibly with some manual fixups), make a PR of the changes and land it!

Details

Running rustfmt

First off you'll need to build rustfmt. For that you'll need the source code and an up to date version of the nightly compiler (scroll down to the bottom). Then to build, just run cargo build in the directory where you cloned Rustfmt. You can check it worked by running cargo test.

The most common reasons for a build failing are not using the nightly version of the compiler, or using one which is not new enough - Rustfmt lives on the bleeding edge of Rust development!

You'll also need a clone of the rust repo. Once you have that, pick a module to work on. You'll have to identify the main file for that module which will be lib.rs or mod.rs. Then to reformat, run something like target/debug/rustfmt --write-mode=overwrite ~/rust/src/librustc_trans/save/mod.rs from the directory you installed Rustfmt in (you will need to change the path to reflect the module you want to reformat).

Rustfmt issues

If Rustfmt crashes during formatting, please get a backtrace by re-running with RUST_BACKTRACE=1. File an issue on the Rustfmt repo. If you can make a minimal test case, that is very much appreciated. The crash should tell you which file Rustfmt failed on. My technique for getting a test case is to copy that file to a temporary and cut code until I have the smallest program which still gives the same crash. Note that input to Rustfmt does not have to be valid Rust, it only needs to parse.

If formatting succeeds, then take a look at the diff to see what has been changed. You can use git diff. I find it easier to push to GitHub and look at their diff. If there are places which you think should have been re-formatted, but weren't, or which were formatted poorly, please submit an issue to the Rustfmt repo. Include the diff of the change which you think is poor, or the code which was not reformatted.

Most cases of poor formatting should block landing the changes to the Rust repo. Some cases will be acceptable, but we could do better. In those cases please file an issue and submit a PR too, but leave a reference to the issue with the PR.

Fixups

Some code will need fixing up after Rustfmt is done. There are two reasons for this: in some places Rustfmt won't reformat the code yet, but it moves around surrounding code in a way which makes this a problem. In other places you might want non-standard formatting, for example if you have a 3x3 array which represents a matrix, you might want this on three lines even though rustfmt can fit it on one line.

For the first case you can manually edit the code. Check that Rustfmt preserves the changed code by running Rustfmt again and checking there are no changes.

For the second case, you can use the #[rustfmt_skip] attribute. This can be placed on functions, modules, and most other items. Again, after fixing up the source code and adding the attribute, check that Rustfmt does not make any further changes (and report as a bug if it does).

Rust PRs

And if it all works out, then submit a PR to Rust! Do this from your branch on GitHub. When submitting the PR, you can put r? @nrc in the first PR comment (or any subsequent comment) to ensure I get pinged for review. Or, if you know the files you worked on are frequently reviewed by a particular person, you can use the same method to request review from them. In that case, please cc @nrc so that I can keep track of what is getting formatted.

Examples

Some example PRs I've done and the issues filed on Rustfmt: