Skip to content

Commit f9532ba

Browse files
davidbarskynrc
authored andcommitted
Implemented rough draft of check write mode. (#2539)
* Add rough draft of `check` mode. Not unit tested. * Added assert-cli; simple test case for `--write-mode=check` * Lightly documented `check` WriteMode * wrote clearer phrasing for config::options::WriteMode::Check * Implemented default for WriteMode where default is Overwrite * Simplified exit code handling * updated README.md as per @nrc' comment * collapsed exit-code handling * Removed write_mode from Summary, introduced partial option parsing earlier * Handle write-mode parsing in a slightly better way.
1 parent fe29b2a commit f9532ba

File tree

10 files changed

+252
-75
lines changed

10 files changed

+252
-75
lines changed

Cargo.lock

+178-59
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ rustc-ap-syntax = "103.0.0"
5050

5151
[dev-dependencies]
5252
lazy_static = "1.0.0"
53+
assert_cli = "0.5"
5354

5455
[target.'cfg(unix)'.dependencies]
5556
libc = "0.2.11"

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,17 @@ read data from stdin. Alternatively, you can use `cargo fmt` to format all
106106
binary and library targets of your crate.
107107

108108
You'll probably want to specify the write mode. Currently, there are modes for
109-
`diff`, `replace`, `overwrite`, `display`, `coverage`, `checkstyle`, and `plain`.
109+
`check`, `diff`, `replace`, `overwrite`, `display`, `coverage`, `checkstyle`, and `plain`.
110110

111111
* `overwrite` Is the default and overwrites the original files _without_ creating backups.
112112
* `replace` Overwrites the original files after creating backups of the files.
113113
* `display` Will print the formatted files to stdout.
114114
* `plain` Also writes to stdout, but with no metadata.
115115
* `diff` Will print a diff between the original files and formatted files to stdout.
116116
Will also exit with an error code if there are any differences.
117+
* `check` Checks if the program's formatting matches what rustfmt would do. Silently exits
118+
with code 0 if so, emits a diff and exits with code 1 if not. This option is
119+
designed to be run in CI-like where a non-zero exit signifies incorrect formatting.
117120
* `checkstyle` Will output the lines that need to be corrected as a checkstyle XML file,
118121
that can be used by tools like Jenkins.
119122

src/bin/main.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use rustfmt::{run, FileName, Input, Summary};
3030
type FmtError = Box<error::Error + Send + Sync>;
3131
type FmtResult<T> = std::result::Result<T, FmtError>;
3232

33-
const WRITE_MODE_LIST: &str = "[replace|overwrite|display|plain|diff|coverage|checkstyle]";
33+
const WRITE_MODE_LIST: &str = "[replace|overwrite|display|plain|diff|coverage|checkstyle|check]";
3434

3535
/// Rustfmt operations.
3636
enum Operation {
@@ -303,6 +303,7 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
303303
let mut out = &mut stdout();
304304
checkstyle::output_header(&mut out, config.write_mode())?;
305305
let mut error_summary = Summary::default();
306+
306307
for file in files {
307308
if !file.exists() {
308309
eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
@@ -350,22 +351,28 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
350351
}
351352
}
352353

354+
fn determine_write_mode(opts: &Options) -> WriteMode {
355+
let matches = opts.parse(env::args().skip(1)).unwrap();
356+
let options = CliOptions::from_matches(&matches).unwrap();
357+
match options.write_mode {
358+
Some(m) => m,
359+
None => WriteMode::default(),
360+
}
361+
}
362+
353363
fn main() {
354364
env_logger::init();
355-
356365
let opts = make_opts();
366+
// Only handles arguments passed in through the CLI.
367+
let write_mode = determine_write_mode(&opts);
357368

358369
let exit_code = match execute(&opts) {
359370
Ok(summary) => {
360-
if summary.has_operational_errors() {
371+
if summary.has_operational_errors()
372+
|| summary.has_diff && write_mode == WriteMode::Check
373+
|| summary.has_parsing_errors() || summary.has_formatting_errors()
374+
{
361375
1
362-
} else if summary.has_parsing_errors() {
363-
2
364-
} else if summary.has_formatting_errors() {
365-
3
366-
} else if summary.has_diff {
367-
// should only happen in diff mode
368-
4
369376
} else {
370377
assert!(summary.has_no_errors());
371378
0

src/config/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ create_config! {
120120
// Control options (changes the operation of rustfmt, rather than the formatting)
121121
write_mode: WriteMode, WriteMode::Overwrite, false,
122122
"What Write Mode to use when none is supplied: \
123-
Replace, Overwrite, Display, Plain, Diff, Coverage";
123+
Replace, Overwrite, Display, Plain, Diff, Coverage, Check";
124124
color: Color, Color::Auto, false,
125125
"What Color option to use when none is supplied: Always, Never, Auto";
126126
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,

src/config/options.rs

+9
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ configuration_option_enum! { WriteMode:
183183
Checkstyle,
184184
// Output the changed lines (for internal value only)
185185
Modified,
186+
// Checks if a diff can be generated. If so, rustfmt outputs a diff and quits with exit code 1.
187+
// This option is designed to be run in CI where a non-zero exit signifies non-standard code formatting.
188+
Check,
186189
}
187190

188191
configuration_option_enum! { Color:
@@ -250,6 +253,12 @@ impl ::std::str::FromStr for WidthHeuristics {
250253
}
251254
}
252255

256+
impl Default for WriteMode {
257+
fn default() -> WriteMode {
258+
WriteMode::Overwrite
259+
}
260+
}
261+
253262
/// A set of directories, files and modules that rustfmt should ignore.
254263
#[derive(Default, Deserialize, Serialize, Clone, Debug)]
255264
pub struct IgnoreList(HashSet<PathBuf>);

src/config/summary.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@ impl Summary {
104104
pub fn print_exit_codes() {
105105
let exit_codes = r#"Exit Codes:
106106
0 = No errors
107-
1 = Encountered operational errors e.g. an IO error
108-
2 = Failed to reformat code because of parsing errors
109-
3 = Code is valid, but it is impossible to format it properly
110-
4 = Formatted code differs from existing code (write-mode diff only)"#;
107+
1 = Encountered error in formatting code"#;
111108
println!("{}", exit_codes);
112109
}
113110
}

src/filemap.rs

+13
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,19 @@ where
178178
let diff = create_diff(filename, text, config)?;
179179
output_checkstyle_file(out, filename, diff)?;
180180
}
181+
WriteMode::Check => {
182+
let filename = filename_to_path();
183+
if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
184+
let mismatch = make_diff(&ori, &fmt, 3);
185+
let has_diff = !mismatch.is_empty();
186+
print_diff(
187+
mismatch,
188+
|line_num| format!("Diff in {} at line {}:", filename.display(), line_num),
189+
config.color(),
190+
);
191+
return Ok(has_diff);
192+
}
193+
}
181194
}
182195

183196
// when we are not in diff mode, don't indicate differing files

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![allow(stable_features)]
1515
#![feature(match_default_bindings)]
1616
#![feature(type_ascription)]
17+
#![feature(unicode_internals)]
1718

1819
#[macro_use]
1920
extern crate derive_new;

tests/lib.rs

+27
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
extern crate assert_cli;
1112
#[macro_use]
1213
extern crate lazy_static;
1314
#[macro_use]
@@ -862,3 +863,29 @@ fn configuration_snippet_tests() {
862863
println!("Ran {} configurations tests.", blocks.len());
863864
assert_eq!(failures, 0, "{} configurations tests failed", failures);
864865
}
866+
867+
#[test]
868+
fn verify_check_works() {
869+
assert_cli::Assert::command(&[
870+
"cargo",
871+
"run",
872+
"--bin=rustfmt",
873+
"--",
874+
"--write-mode=check",
875+
"src/bin/main.rs",
876+
]).succeeds()
877+
.unwrap();
878+
}
879+
880+
#[test]
881+
fn verify_diff_works() {
882+
assert_cli::Assert::command(&[
883+
"cargo",
884+
"run",
885+
"--bin=rustfmt",
886+
"--",
887+
"--write-mode=diff",
888+
"src/bin/main.rs",
889+
]).succeeds()
890+
.unwrap();
891+
}

0 commit comments

Comments
 (0)