1 use shell_escape::escape;
4 use std::path::{Path, PathBuf};
5 use std::process::{self, Command};
10 CommandFailed(String),
13 WalkDirError(walkdir::Error),
16 impl From<io::Error> for CliError {
17 fn from(error: io::Error) -> Self {
22 impl From<walkdir::Error> for CliError {
23 fn from(error: walkdir::Error) -> Self {
24 Self::WalkDirError(error)
33 pub fn run(check: bool, verbose: bool) {
34 fn try_run(context: &FmtContext) -> Result<bool, CliError> {
35 let mut success = true;
37 let project_root = project_root()?;
39 success &= cargo_fmt(context, project_root.as_path())?;
40 success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
41 success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
43 for entry in WalkDir::new(project_root.join("tests")) {
45 let path = entry.path();
47 if path.extension() != Some("rs".as_ref())
48 || entry.file_name() == "ice-3891.rs"
49 // Avoid rustfmt bug rust-lang/rustfmt#1873
50 || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
55 success &= rustfmt(context, &path)?;
61 fn output_err(err: CliError) {
63 CliError::CommandFailed(command) => {
64 eprintln!("error: A command failed! `{}`", command);
66 CliError::IoError(err) => {
67 eprintln!("error: {}", err);
69 CliError::ProjectRootNotFound => {
70 eprintln!("error: Can't determine root of project. Please run inside a Clippy working dir.");
72 CliError::WalkDirError(err) => {
73 eprintln!("error: {}", err);
78 let context = FmtContext { check, verbose };
79 let result = try_run(&context);
80 let code = match result {
84 eprintln!("Formatting check failed.");
85 eprintln!("Run `./util/dev fmt` to update formatting.");
96 fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
97 let arg_display: Vec<_> = args
99 .map(|a| escape(a.as_ref().to_string_lossy()).to_owned())
104 escape(dir.as_ref().to_string_lossy()),
105 escape(program.as_ref().to_string_lossy()),
106 arg_display.join(" ")
111 context: &FmtContext,
112 program: impl AsRef<OsStr>,
113 dir: impl AsRef<Path>,
114 args: &[impl AsRef<OsStr>],
115 ) -> Result<bool, CliError> {
117 println!("{}", format_command(&program, &dir, args));
120 let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
121 let code = child.wait()?;
122 let success = code.success();
124 if !context.check && !success {
125 return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
131 fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
132 let mut args = vec!["+nightly", "fmt", "--all"];
135 args.push("--check");
137 let success = exec(context, "cargo", path, &args)?;
142 fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
143 let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
145 args.push("--check".as_ref());
147 let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
149 eprintln!("rustfmt failed on {}", path.display());
154 fn project_root() -> Result<PathBuf, CliError> {
155 let current_dir = std::env::current_dir()?;
156 for path in current_dir.ancestors() {
157 let result = std::fs::read_to_string(path.join("Cargo.toml"));
158 if let Err(err) = &result {
159 if err.kind() == io::ErrorKind::NotFound {
164 let content = result?;
165 if content.contains("[package]\nname = \"clippy\"") {
166 return Ok(path.to_path_buf());
170 Err(CliError::ProjectRootNotFound)