Fix shell welcome prompt showing up in Zed's stdout (#41311)

Anthony Eid created

The bug occurred because `smol::process::Command::from(_)` doesn't set
the correct fields for stdio markers. So moving the stdio configuration
after converting to a `smol` command fixed the issue.

I added the `std::process::Command::{stdout, stderr, stdin}` functions
to our disallowed list in clippy to prevent any bugs like this appearing
in the future.

Release Notes:

- N/A

Change summary

clippy.toml                  |  3 +++
crates/util/src/shell_env.rs | 12 ++++++------
2 files changed, 9 insertions(+), 6 deletions(-)

Detailed changes

clippy.toml 🔗

@@ -9,6 +9,9 @@ disallowed-methods = [
     { path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" },
     { path = "std::process::Command::output", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::output" },
     { path = "std::process::Command::status", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::status" },
+    { path = "std::process::Command::stdin", reason = "`smol::process::Command::from()` does not preserve stdio configuration", replacement = "smol::process::Command::stdin" },
+    { path = "std::process::Command::stdout", reason = "`smol::process::Command::from()` does not preserve stdio configuration", replacement = "smol::process::Command::stdout" },
+    { path = "std::process::Command::stderr", reason = "`smol::process::Command::from()` does not preserve stdio configuration", replacement = "smol::process::Command::stderr" },
     { path = "serde_json::from_reader", reason = "Parsing from a buffer is much slower than first reading the buffer into a Vec/String, see https://github.com/serde-rs/json/issues/160#issuecomment-253446892. Use `serde_json::from_slice` instead." },
     { path = "serde_json_lenient::from_reader", reason = "Parsing from a buffer is much slower than first reading the buffer into a Vec/String, see https://github.com/serde-rs/json/issues/160#issuecomment-253446892, Use `serde_json_lenient::from_slice` instead." },
 ]

crates/util/src/shell_env.rs 🔗

@@ -33,7 +33,6 @@ async fn capture_unix(
     directory: &Path,
 ) -> Result<collections::HashMap<String, String>> {
     use std::os::unix::process::CommandExt;
-    use std::process::Stdio;
 
     let shell_kind = ShellKind::new(shell_path, false);
     let zed_path = super::get_shell_safe_zed_path(shell_kind)?;
@@ -56,9 +55,6 @@ async fn capture_unix(
         ShellKind::Xonsh => (FD_STDERR, "o>e".to_string()),
         _ => (FD_STDIN, format!(">&{}", FD_STDIN)), // `>&0`
     };
-    command.stdin(Stdio::null());
-    command.stdout(Stdio::piped());
-    command.stderr(Stdio::piped());
 
     match shell_kind {
         ShellKind::Csh | ShellKind::Tcsh => {
@@ -107,7 +103,7 @@ async fn spawn_and_read_fd(
     child_fd: std::os::fd::RawFd,
 ) -> anyhow::Result<(Vec<u8>, std::process::Output)> {
     use command_fds::{CommandFdExt, FdMapping};
-    use std::io::Read;
+    use std::{io::Read, process::Stdio};
 
     let (mut reader, writer) = std::io::pipe()?;
 
@@ -116,7 +112,11 @@ async fn spawn_and_read_fd(
         child_fd,
     }])?;
 
-    let process = smol::process::Command::from(command).spawn()?;
+    let process = smol::process::Command::from(command)
+        .stdin(Stdio::null())
+        .stdout(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()?;
 
     let mut buffer = Vec::new();
     reader.read_to_end(&mut buffer)?;