1 use crate::clippy_project_root;
2 use shell_escape::escape;
5 use std::process::{self, Command};
11 CommandFailed(String, String),
14 WalkDirError(walkdir::Error),
18 impl From<io::Error> for CliError {
19 fn from(error: io::Error) -> Self {
24 impl From<walkdir::Error> for CliError {
25 fn from(error: walkdir::Error) -> Self {
26 Self::WalkDirError(error)
35 // the "main" function of cargo dev fmt
36 pub fn run(check: bool, verbose: bool) {
37 fn try_run(context: &FmtContext) -> Result<bool, CliError> {
38 let mut success = true;
40 let project_root = clippy_project_root();
42 // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
43 // format because rustfmt would also format the entire rustc repo as it is a local
45 if fs::read_to_string(project_root.join("Cargo.toml"))
46 .expect("Failed to read clippy Cargo.toml")
47 .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
49 return Err(CliError::RaSetupActive);
52 rustfmt_test(context)?;
54 success &= cargo_fmt(context, project_root.as_path())?;
55 success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
56 success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
57 success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
59 for entry in WalkDir::new(project_root.join("tests")) {
61 let path = entry.path();
63 if path.extension() != Some("rs".as_ref())
64 || entry.file_name() == "ice-3891.rs"
65 // Avoid rustfmt bug rust-lang/rustfmt#1873
66 || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
71 success &= rustfmt(context, &path)?;
77 fn output_err(err: CliError) {
79 CliError::CommandFailed(command, stderr) => {
80 eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr);
82 CliError::IoError(err) => {
83 eprintln!("error: {}", err);
85 CliError::RustfmtNotInstalled => {
86 eprintln!("error: rustfmt nightly is not installed.");
88 CliError::WalkDirError(err) => {
89 eprintln!("error: {}", err);
91 CliError::RaSetupActive => {
93 "error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
94 Not formatting because that would format the local repo as well!
95 Please revert the changes to Cargo.tomls first."
101 let context = FmtContext { check, verbose };
102 let result = try_run(&context);
103 let code = match result {
107 eprintln!("Formatting check failed.");
108 eprintln!("Run `cargo dev fmt` to update formatting.");
119 fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
120 let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
124 escape(dir.as_ref().to_string_lossy()),
125 escape(program.as_ref().to_string_lossy()),
126 arg_display.join(" ")
131 context: &FmtContext,
132 program: impl AsRef<OsStr>,
133 dir: impl AsRef<Path>,
134 args: &[impl AsRef<OsStr>],
135 ) -> Result<bool, CliError> {
137 println!("{}", format_command(&program, &dir, args));
140 let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
141 let output = child.wait_with_output()?;
142 let success = output.status.success();
144 if !context.check && !success {
145 let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
146 return Err(CliError::CommandFailed(
147 format_command(&program, &dir, args),
148 String::from(stderr),
155 fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
156 let mut args = vec!["+nightly", "fmt", "--all"];
159 args.push("--check");
161 let success = exec(context, "cargo", path, &args)?;
166 fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
167 let program = "rustfmt";
168 let dir = std::env::current_dir()?;
169 let args = &["+nightly", "--version"];
172 println!("{}", format_command(&program, &dir, args));
175 let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
177 if output.status.success() {
179 } else if std::str::from_utf8(&output.stderr)
181 .starts_with("error: 'rustfmt' is not installed")
183 Err(CliError::RustfmtNotInstalled)
185 Err(CliError::CommandFailed(
186 format_command(&program, &dir, args),
187 std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
192 fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
193 let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
195 args.push("--check".as_ref());
197 let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
199 eprintln!("rustfmt failed on {}", path.display());