Skip to content

Commit 685ad6e

Browse files
committed
clippy_dev: refactor rustfmt calls
1 parent be985a5 commit 685ad6e

9 files changed

+152
-199
lines changed

clippy_dev/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ clap = { version = "4.4", features = ["derive"] }
1111
indoc = "1.0"
1212
itertools = "0.12"
1313
opener = "0.7"
14-
shell-escape = "0.1"
1514
walkdir = "2.3"
1615

1716
[package.metadata.rust-analyzer]

clippy_dev/src/fmt.rs

+83-173
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1+
use crate::utils::{ClippyInfo, ErrAction, UpdateMode, panic_action, run_with_args_split, run_with_output};
12
use itertools::Itertools;
23
use rustc_lexer::{TokenKind, tokenize};
3-
use shell_escape::escape;
4-
use std::ffi::{OsStr, OsString};
54
use std::ops::ControlFlow;
6-
use std::path::{Path, PathBuf};
5+
use std::path::PathBuf;
76
use std::process::{self, Command, Stdio};
87
use std::{fs, io};
98
use walkdir::WalkDir;
109

1110
pub enum Error {
12-
CommandFailed(String, String),
1311
Io(io::Error),
14-
RustfmtNotInstalled,
15-
WalkDir(walkdir::Error),
16-
IntellijSetupActive,
1712
Parse(PathBuf, usize, String),
1813
CheckFailed,
1914
}
@@ -24,50 +19,22 @@ impl From<io::Error> for Error {
2419
}
2520
}
2621

27-
impl From<walkdir::Error> for Error {
28-
fn from(error: walkdir::Error) -> Self {
29-
Self::WalkDir(error)
30-
}
31-
}
32-
3322
impl Error {
3423
fn display(&self) {
3524
match self {
3625
Self::CheckFailed => {
3726
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
3827
},
39-
Self::CommandFailed(command, stderr) => {
40-
eprintln!("error: command `{command}` failed!\nstderr: {stderr}");
41-
},
4228
Self::Io(err) => {
4329
eprintln!("error: {err}");
4430
},
45-
Self::RustfmtNotInstalled => {
46-
eprintln!("error: rustfmt nightly is not installed.");
47-
},
48-
Self::WalkDir(err) => {
49-
eprintln!("error: {err}");
50-
},
51-
Self::IntellijSetupActive => {
52-
eprintln!(
53-
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
54-
Not formatting because that would format the local repo as well!\n\
55-
Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
56-
);
57-
},
5831
Self::Parse(path, line, msg) => {
5932
eprintln!("error parsing `{}:{line}`: {msg}", path.display());
6033
},
6134
}
6235
}
6336
}
6437

65-
struct FmtContext {
66-
check: bool,
67-
verbose: bool,
68-
rustfmt_path: String,
69-
}
70-
7138
struct ClippyConf<'a> {
7239
name: &'a str,
7340
attrs: &'a str,
@@ -257,155 +224,98 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
257224
Ok(())
258225
}
259226

260-
fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
261-
// if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
262-
// format because rustfmt would also format the entire rustc repo as it is a local
263-
// dependency
264-
if fs::read_to_string("Cargo.toml")
265-
.expect("Failed to read clippy Cargo.toml")
266-
.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
267-
{
268-
return Err(Error::IntellijSetupActive);
269-
}
270-
271-
check_for_rustfmt(context)?;
227+
fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
228+
let mut rustfmt_path = String::from_utf8(run_with_output(
229+
"rustup which rustfmt",
230+
Command::new("rustup").args(["which", "rustfmt"]),
231+
))
232+
.expect("invalid rustfmt path");
233+
rustfmt_path.truncate(rustfmt_path.trim_end().len());
272234

273-
cargo_fmt(context, ".".as_ref())?;
274-
cargo_fmt(context, "clippy_dev".as_ref())?;
275-
cargo_fmt(context, "rustc_tools_util".as_ref())?;
276-
cargo_fmt(context, "lintcheck".as_ref())?;
235+
let mut cargo_path = String::from_utf8(run_with_output(
236+
"rustup which cargo",
237+
Command::new("rustup").args(["which", "cargo"]),
238+
))
239+
.expect("invalid cargo path");
240+
cargo_path.truncate(cargo_path.trim_end().len());
241+
242+
// Start all format jobs first before waiting on the results.
243+
let mut children = Vec::with_capacity(16);
244+
for &path in &[".", "clippy_dev", "rustc_tools_util", "lintcheck"] {
245+
let mut cmd = Command::new(&cargo_path);
246+
if update_mode.is_check() {
247+
cmd.arg("--check");
248+
}
249+
match cmd
250+
.current_dir(clippy.path.join(path))
251+
.arg("fmt")
252+
.env("RUSTFMT", &rustfmt_path)
253+
.stdout(Stdio::null())
254+
.stdin(Stdio::null())
255+
.stderr(Stdio::inherit())
256+
.spawn()
257+
{
258+
Ok(x) => children.push(("cargo fmt", x)),
259+
Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()),
260+
}
261+
}
277262

278-
let chunks = WalkDir::new("tests")
279-
.into_iter()
280-
.filter_map(|entry| {
281-
let entry = entry.expect("failed to find tests");
282-
let path = entry.path();
283-
if path.extension() != Some("rs".as_ref())
284-
|| path
285-
.components()
286-
.nth_back(1)
287-
.is_some_and(|c| c.as_os_str() == "syntax-error-recovery")
288-
|| entry.file_name() == "ice-3891.rs"
289-
{
290-
None
291-
} else {
292-
Some(entry.into_path().into_os_string())
263+
run_with_args_split(
264+
|| {
265+
let mut cmd = Command::new(&rustfmt_path);
266+
if update_mode.is_check() {
267+
cmd.arg("--check");
293268
}
294-
})
295-
.chunks(250);
269+
// Inherit stderr https://github.com/rust-lang/rustfmt/issues/6543 is fixed.
270+
cmd.stdout(Stdio::null()).stdin(Stdio::null()).stderr(Stdio::null());
271+
cmd
272+
},
273+
|cmd| match cmd.spawn() {
274+
Ok(x) => children.push(("rustfmt", x)),
275+
Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()),
276+
},
277+
WalkDir::new("tests")
278+
.into_iter()
279+
.filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt"))
280+
.filter_map(|e| {
281+
let e = e.expect("error reading `tests`");
282+
e.path()
283+
.as_os_str()
284+
.as_encoded_bytes()
285+
.ends_with(b".rs")
286+
.then(|| e.into_path().into_os_string())
287+
}),
288+
);
296289

297-
for chunk in &chunks {
298-
rustfmt(context, chunk)?;
290+
for (name, child) in &mut children {
291+
match child.wait() {
292+
Ok(status) => match (update_mode, status.exit_ok()) {
293+
(UpdateMode::Check | UpdateMode::Change, Ok(())) => {},
294+
(UpdateMode::Check, Err(_)) => {
295+
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
296+
process::exit(1);
297+
},
298+
(UpdateMode::Change, Err(e)) => panic_action(&e, ErrAction::Run, name.as_ref()),
299+
},
300+
Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()),
301+
}
299302
}
300-
Ok(())
301303
}
302304

303305
// the "main" function of cargo dev fmt
304-
pub fn run(check: bool, verbose: bool) {
305-
let output = Command::new("rustup")
306-
.args(["which", "rustfmt"])
307-
.stderr(Stdio::inherit())
308-
.output()
309-
.expect("error running `rustup which rustfmt`");
310-
if !output.status.success() {
311-
eprintln!("`rustup which rustfmt` did not execute successfully");
312-
process::exit(1);
306+
pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) {
307+
if clippy.has_intellij_hook {
308+
eprintln!(
309+
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
310+
Not formatting because that would format the local repo as well!\n\
311+
Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
312+
);
313+
return;
313314
}
314-
let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path");
315-
rustfmt_path.truncate(rustfmt_path.trim_end().len());
315+
run_rustfmt(clippy, update_mode);
316316

317-
let context = FmtContext {
318-
check,
319-
verbose,
320-
rustfmt_path,
321-
};
322-
if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) {
317+
if let Err(e) = fmt_conf(update_mode.is_check()) {
323318
e.display();
324319
process::exit(1);
325320
}
326321
}
327-
328-
fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
329-
let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
330-
331-
format!(
332-
"cd {} && {} {}",
333-
escape(dir.as_ref().to_string_lossy()),
334-
escape(program.as_ref().to_string_lossy()),
335-
arg_display.join(" ")
336-
)
337-
}
338-
339-
fn exec_fmt_command(
340-
context: &FmtContext,
341-
program: impl AsRef<OsStr>,
342-
dir: impl AsRef<Path>,
343-
args: &[impl AsRef<OsStr>],
344-
) -> Result<(), Error> {
345-
if context.verbose {
346-
println!("{}", format_command(&program, &dir, args));
347-
}
348-
349-
let output = Command::new(&program)
350-
.env("RUSTFMT", &context.rustfmt_path)
351-
.current_dir(&dir)
352-
.args(args.iter())
353-
.output()
354-
.unwrap();
355-
let success = output.status.success();
356-
357-
match (context.check, success) {
358-
(_, true) => Ok(()),
359-
(true, false) => Err(Error::CheckFailed),
360-
(false, false) => {
361-
let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
362-
Err(Error::CommandFailed(
363-
format_command(&program, &dir, args),
364-
String::from(stderr),
365-
))
366-
},
367-
}
368-
}
369-
370-
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> {
371-
let mut args = vec!["fmt", "--all"];
372-
if context.check {
373-
args.push("--check");
374-
}
375-
exec_fmt_command(context, "cargo", path, &args)
376-
}
377-
378-
fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> {
379-
let program = "rustfmt";
380-
let dir = std::env::current_dir()?;
381-
let args = &["--version"];
382-
383-
if context.verbose {
384-
println!("{}", format_command(program, &dir, args));
385-
}
386-
387-
let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
388-
389-
if output.status.success() {
390-
Ok(())
391-
} else if std::str::from_utf8(&output.stderr)
392-
.unwrap_or("")
393-
.starts_with("error: 'rustfmt' is not installed")
394-
{
395-
Err(Error::RustfmtNotInstalled)
396-
} else {
397-
Err(Error::CommandFailed(
398-
format_command(program, &dir, args),
399-
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
400-
))
401-
}
402-
}
403-
404-
fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<(), Error> {
405-
let mut args = Vec::new();
406-
if context.check {
407-
args.push(OsString::from("--check"));
408-
}
409-
args.extend(paths);
410-
exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args)
411-
}

clippy_dev/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(rustc_private, if_let_guard, let_chains)]
1+
#![feature(rustc_private, exit_status_error, if_let_guard, let_chains)]
22
#![warn(
33
trivial_casts,
44
trivial_numeric_casts,

clippy_dev/src/main.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn main() {
2626
allow_staged,
2727
allow_no_vcs,
2828
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
29-
DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
29+
DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)),
3030
DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)),
3131
DevCommand::NewLint {
3232
pass,
@@ -125,9 +125,6 @@ enum DevCommand {
125125
#[arg(long)]
126126
/// Use the rustfmt --check option
127127
check: bool,
128-
#[arg(short, long)]
129-
/// Echo commands run
130-
verbose: bool,
131128
},
132129
#[command(name = "update_lints")]
133130
/// Updates lint registration and information from the source code

clippy_dev/src/update_lints.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::utils::{
2-
File, FileAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_file, update_text_region_fn,
2+
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn,
33
};
44
use itertools::Itertools;
55
use rustc_lexer::unescape;
@@ -173,7 +173,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirE
173173
WalkDir::new(src_root).into_iter().filter_map(move |e| {
174174
let e = match e {
175175
Ok(e) => e,
176-
Err(ref e) => panic_file(e, FileAction::Read, src_root),
176+
Err(ref e) => panic_action(e, ErrAction::Read, src_root),
177177
};
178178
let path = e.path().as_os_str().as_encoded_bytes();
179179
if let Some(path) = path.strip_suffix(b".rs")
@@ -220,9 +220,7 @@ fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec<Lint>)
220220
while searcher.find_token(Ident("declare_clippy_lint")) {
221221
let start = searcher.pos() as usize - "declare_clippy_lint".len();
222222
let (mut name, mut group) = ("", "");
223-
if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group])
224-
&& searcher.find_token(CloseBrace)
225-
{
223+
if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) {
226224
lints.push(Lint {
227225
name: name.to_lowercase(),
228226
group: group.into(),

0 commit comments

Comments
 (0)