Repository: rust-lang/rustfmt (rust)

Issue:
regression: rustfmt using input from stdin emits empty output when using `#![rustfmt::skip]`
Since #5094 - rustfmt now emits nothing to stdout if reading a file with `#![rustfmt::skip]` is provided via stdin.

For example, running `echo '#![rustfmt::skip]\n\nfn main() { }'  | rustfmt` no longer emits anything to stdout. 

Rust-analyzer uses rustfmt to format files, and sends rustfmt the file contents to format via stdin. 

This has caused an interesting issue, where rustfmt emits nothing, so rust-analyzer deletes the contents of the file being formatted: https://github.com/rust-lang/rust-analyzer/issues/12458



Pull request description:
fix handling of inner ignore attributes on stdin
Fixes #5368 

Patch:
diff --git a/src/formatting.rs b/src/formatting.rs
index e6995210..23d90d9e 100644
--- a/src/formatting.rs
+++ b/src/formatting.rs
@@ -40,12 +40,10 @@ impl<'b, T: Write + 'b> Session<'b, T> {
         rustc_span::create_session_if_not_set_then(self.config.edition().into(), |_| {
             if self.config.disable_all_formatting() {
                 // When the input is from stdin, echo back the input.
-                if let Input::Text(ref buf) = input {
-                    if let Err(e) = io::stdout().write_all(buf.as_bytes()) {
-                        return Err(From::from(e));
-                    }
-                }
-                return Ok(FormatReport::new());
+                return match input {
+                    Input::Text(ref buf) => echo_back_stdin(buf),
+                    _ => Ok(FormatReport::new()),
+                };
             }
 
             let config = &self.config.clone();
@@ -94,6 +92,13 @@ fn should_skip_module<T: FormatHandler>(
     false
 }
 
+fn echo_back_stdin(input: &str) -> Result<FormatReport, ErrorKind> {
+    if let Err(e) = io::stdout().write_all(input.as_bytes()) {
+        return Err(From::from(e));
+    }
+    Ok(FormatReport::new())
+}
+
 // Format an entire crate (or subset of the module tree).
 fn format_project<T: FormatHandler>(
     input: Input,
@@ -136,7 +141,8 @@ fn format_project<T: FormatHandler>(
     .visit_crate(&krate)?
     .into_iter()
     .filter(|(path, module)| {
-        !should_skip_module(config, &context, input_is_stdin, &main_file, path, module)
+        input_is_stdin
+            || !should_skip_module(config, &context, input_is_stdin, &main_file, path, module)
     })
     .collect::<Vec<_>>();
 
@@ -146,6 +152,14 @@ fn format_project<T: FormatHandler>(
     context.parse_session.set_silent_emitter();
 
     for (path, module) in files {
+        if input_is_stdin && contains_skip(module.attrs()) {
+            return echo_back_stdin(
+                context
+                    .parse_session
+                    .snippet_provider(module.span)
+                    .entire_snippet(),
+            );
+        }
         should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path));
         context.format_file(path, &module, is_macro_def)?;
     }


Test patch:
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 0eda9e4b..18ec8620 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -578,6 +578,30 @@ fn stdin_generated_files_issue_5172() {
     );
 }
 
+#[test]
+fn stdin_handles_mod_inner_ignore_attr() {
+    // see https://github.com/rust-lang/rustfmt/issues/5368
+    init_log();
+    let input = String::from("#![rustfmt::skip]\n\nfn    main() {  }");
+    let mut child = Command::new(rustfmt().to_str().unwrap())
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .spawn()
+        .expect("failed to execute child");
+
+    {
+        let stdin = child.stdin.as_mut().expect("failed to get stdin");
+        stdin
+            .write_all(input.as_bytes())
+            .expect("failed to write stdin");
+    }
+
+    let output = child.wait_with_output().expect("failed to wait on child");
+    assert!(output.status.success());
+    assert!(output.stderr.is_empty());
+    assert_eq!(input, String::from_utf8(output.stdout).unwrap());
+}
+
 #[test]
 fn format_lines_errors_are_reported() {
     init_log();