Skip to content

Commit e4a50da

Browse files
jugglerchristopecongiro
authored andcommitted
Make --check work when running from stdin. (#3896)
1 parent 6aec17e commit e4a50da

File tree

7 files changed

+139
-26
lines changed

7 files changed

+139
-26
lines changed

Diff for: src/bin/main.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,10 @@ pub enum OperationError {
7575
/// An io error during reading or writing.
7676
#[fail(display = "io error: {}", _0)]
7777
IoError(IoError),
78-
/// Attempt to use --check with stdin, which isn't currently
79-
/// supported.
80-
#[fail(display = "The `--check` option is not supported with standard input.")]
81-
CheckWithStdin,
82-
/// Attempt to use --emit=json with stdin, which isn't currently
83-
/// supported.
84-
#[fail(display = "Using `--emit` other than stdout is not supported with standard input.")]
85-
EmitWithStdin,
78+
/// Attempt to use --emit with a mode which is not currently
79+
/// supported with stdandard input.
80+
#[fail(display = "Emit mode {} not supported with standard output.", _0)]
81+
StdinBadEmit(EmitMode),
8682
}
8783

8884
impl From<IoError> for OperationError {
@@ -253,15 +249,20 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32, FailureE
253249
let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
254250

255251
if options.check {
256-
return Err(OperationError::CheckWithStdin.into());
257-
}
258-
if let Some(emit_mode) = options.emit_mode {
259-
if emit_mode != EmitMode::Stdout {
260-
return Err(OperationError::EmitWithStdin.into());
252+
config.set().emit_mode(EmitMode::Diff);
253+
} else {
254+
match options.emit_mode {
255+
// Emit modes which work with standard input
256+
// None means default, which is Stdout.
257+
None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {}
258+
Some(emit_mode) => {
259+
return Err(OperationError::StdinBadEmit(emit_mode).into());
260+
}
261261
}
262+
config
263+
.set()
264+
.emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout));
262265
}
263-
// emit mode is always Stdout for Stdin.
264-
config.set().emit_mode(EmitMode::Stdout);
265266
config.set().verbose(Verbosity::Quiet);
266267

267268
// parse file_lines

Diff for: src/emitter/checkstyle.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use self::xml::XmlEscaped;
22
use super::*;
33
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
44
use std::io::{self, Write};
5-
use std::path::Path;
65

76
mod xml;
87

@@ -30,7 +29,6 @@ impl Emitter for CheckstyleEmitter {
3029
}: FormattedFile<'_>,
3130
) -> Result<EmitterResult, io::Error> {
3231
const CONTEXT_SIZE: usize = 0;
33-
let filename = ensure_real_path(filename);
3432
let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
3533
output_checkstyle_file(output, filename, diff)?;
3634
Ok(EmitterResult::default())
@@ -39,13 +37,13 @@ impl Emitter for CheckstyleEmitter {
3937

4038
pub(crate) fn output_checkstyle_file<T>(
4139
mut writer: T,
42-
filename: &Path,
40+
filename: &FileName,
4341
diff: Vec<Mismatch>,
4442
) -> Result<(), io::Error>
4543
where
4644
T: Write,
4745
{
48-
write!(writer, r#"<file name="{}">"#, filename.display())?;
46+
write!(writer, r#"<file name="{}">"#, filename)?;
4947
for mismatch in diff {
5048
let begin_line = mismatch.line_number;
5149
let mut current_line;
@@ -77,7 +75,11 @@ mod tests {
7775
fn emits_empty_record_on_file_with_no_mismatches() {
7876
let file_name = "src/well_formatted.rs";
7977
let mut writer = Vec::new();
80-
let _ = output_checkstyle_file(&mut writer, &PathBuf::from(file_name), vec![]);
78+
let _ = output_checkstyle_file(
79+
&mut writer,
80+
&FileName::Real(PathBuf::from(file_name)),
81+
vec![],
82+
);
8183
assert_eq!(
8284
&writer[..],
8385
format!(r#"<file name="{}"></file>"#, file_name).as_bytes()

Diff for: src/emitter/json.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
33
use serde::Serialize;
44
use serde_json::to_string as to_json_string;
55
use std::io::{self, Write};
6-
use std::path::Path;
76

87
#[derive(Debug, Default)]
98
pub(crate) struct JsonEmitter {
@@ -47,7 +46,6 @@ impl Emitter for JsonEmitter {
4746
}: FormattedFile<'_>,
4847
) -> Result<EmitterResult, io::Error> {
4948
const CONTEXT_SIZE: usize = 0;
50-
let filename = ensure_real_path(filename);
5149
let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
5250
let has_diff = !diff.is_empty();
5351

@@ -62,7 +60,7 @@ impl Emitter for JsonEmitter {
6260

6361
fn output_json_file<T>(
6462
mut writer: T,
65-
filename: &Path,
63+
filename: &FileName,
6664
diff: Vec<Mismatch>,
6765
num_emitted_files: u32,
6866
) -> Result<(), io::Error>
@@ -106,7 +104,7 @@ where
106104
});
107105
}
108106
let json = to_json_string(&MismatchedFile {
109-
name: String::from(filename.to_str().unwrap()),
107+
name: format!("{}", filename),
110108
mismatches,
111109
})?;
112110
let prefix = if num_emitted_files > 0 { "," } else { "" };
@@ -148,7 +146,12 @@ mod tests {
148146

149147
let mut writer = Vec::new();
150148
let exp_json = to_json_string(&mismatched_file).unwrap();
151-
let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
149+
let _ = output_json_file(
150+
&mut writer,
151+
&FileName::Real(PathBuf::from(file)),
152+
vec![mismatch],
153+
0,
154+
);
152155
assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
153156
}
154157

@@ -188,7 +191,12 @@ mod tests {
188191

189192
let mut writer = Vec::new();
190193
let exp_json = to_json_string(&mismatched_file).unwrap();
191-
let _ = output_json_file(&mut writer, &PathBuf::from(file), vec![mismatch], 0);
194+
let _ = output_json_file(
195+
&mut writer,
196+
&FileName::Real(PathBuf::from(file)),
197+
vec![mismatch],
198+
0,
199+
);
192200
assert_eq!(&writer[..], format!("{}", exp_json).as_bytes());
193201
}
194202

Diff for: src/test/mod.rs

+93
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,52 @@ fn assert_output(source: &Path, expected_filename: &Path) {
305305
}
306306
}
307307

308+
// Helper function for comparing the results of rustfmt
309+
// to a known output generated by one of the write modes.
310+
fn assert_stdin_output(
311+
source: &Path,
312+
expected_filename: &Path,
313+
emit_mode: EmitMode,
314+
has_diff: bool,
315+
) {
316+
let mut config = Config::default();
317+
config.set().newline_style(NewlineStyle::Unix);
318+
config.set().emit_mode(emit_mode);
319+
320+
let mut source_file = fs::File::open(&source).expect("couldn't open source");
321+
let mut source_text = String::new();
322+
source_file
323+
.read_to_string(&mut source_text)
324+
.expect("Failed reading target");
325+
let input = Input::Text(source_text);
326+
327+
// Populate output by writing to a vec.
328+
let mut buf: Vec<u8> = vec![];
329+
{
330+
let mut session = Session::new(config, Some(&mut buf));
331+
session.format(input).unwrap();
332+
let errors = ReportedErrors {
333+
has_diff: has_diff,
334+
..Default::default()
335+
};
336+
assert_eq!(session.errors, errors);
337+
}
338+
339+
let mut expected_file = fs::File::open(&expected_filename).expect("couldn't open target");
340+
let mut expected_text = String::new();
341+
expected_file
342+
.read_to_string(&mut expected_text)
343+
.expect("Failed reading target");
344+
345+
let output = String::from_utf8(buf).unwrap();
346+
let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
347+
if !compare.is_empty() {
348+
let mut failures = HashMap::new();
349+
failures.insert(source.to_owned(), compare);
350+
print_mismatches_default_message(failures);
351+
panic!("Text does not match expected output");
352+
}
353+
}
308354
// Idempotence tests. Files in tests/target are checked to be unaltered by
309355
// rustfmt.
310356
#[test]
@@ -428,6 +474,30 @@ fn stdin_works_with_modified_lines() {
428474
assert_eq!(buf, output.as_bytes());
429475
}
430476

477+
/// Ensures that `EmitMode::Json` works with input from `stdin`.
478+
#[test]
479+
fn stdin_works_with_json() {
480+
init_log();
481+
assert_stdin_output(
482+
Path::new("tests/writemode/source/stdin.rs"),
483+
Path::new("tests/writemode/target/stdin.json"),
484+
EmitMode::Json,
485+
true,
486+
);
487+
}
488+
489+
/// Ensures that `EmitMode::Checkstyle` works with input from `stdin`.
490+
#[test]
491+
fn stdin_works_with_checkstyle() {
492+
init_log();
493+
assert_stdin_output(
494+
Path::new("tests/writemode/source/stdin.rs"),
495+
Path::new("tests/writemode/target/stdin.xml"),
496+
EmitMode::Checkstyle,
497+
false,
498+
);
499+
}
500+
431501
#[test]
432502
fn stdin_disable_all_formatting_test() {
433503
init_log();
@@ -865,3 +935,26 @@ fn verify_check_works() {
865935
.status()
866936
.expect("run with check option failed");
867937
}
938+
939+
#[test]
940+
fn verify_check_works_with_stdin() {
941+
init_log();
942+
943+
let mut child = Command::new(rustfmt().to_str().unwrap())
944+
.arg("--check")
945+
.stdin(Stdio::piped())
946+
.stderr(Stdio::piped())
947+
.spawn()
948+
.expect("run with check option failed");
949+
950+
{
951+
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
952+
stdin
953+
.write_all("fn main() {}\n".as_bytes())
954+
.expect("Failed to write to rustfmt --check");
955+
}
956+
let output = child
957+
.wait_with_output()
958+
.expect("Failed to wait on rustfmt child");
959+
assert!(output.status.success());
960+
}

Diff for: tests/writemode/source/stdin.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
fn
3+
some( )
4+
{
5+
}
6+
fn main () {}

Diff for: tests/writemode/target/stdin.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"name":"stdin","mismatches":[{"original_begin_line":1,"original_end_line":6,"expected_begin_line":1,"expected_end_line":2,"original":"\nfn\n some( )\n{\n}\nfn main () {}","expected":"fn some() {}\nfn main() {}"}]}]

Diff for: tests/writemode/target/stdin.xml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<checkstyle version="4.3"><file name="stdin"><error line="1" severity="warning" message="Should be `fn some() {}`" /><error line="2" severity="warning" message="Should be `fn main() {}`" /></file></checkstyle>

0 commit comments

Comments
 (0)