1
+ use crate :: utils:: { ClippyInfo , ErrAction , UpdateMode , panic_action, run_with_args_split, run_with_output} ;
1
2
use itertools:: Itertools ;
2
3
use rustc_lexer:: { TokenKind , tokenize} ;
3
- use shell_escape :: escape ;
4
- use std:: ffi :: { OsStr , OsString } ;
4
+ use std :: fs ;
5
+ use std:: io :: { self , Read } ;
5
6
use std:: ops:: ControlFlow ;
6
- use std:: path:: { Path , PathBuf } ;
7
+ use std:: path:: PathBuf ;
7
8
use std:: process:: { self , Command , Stdio } ;
8
- use std:: { fs, io} ;
9
9
use walkdir:: WalkDir ;
10
10
11
11
pub enum Error {
12
- CommandFailed ( String , String ) ,
13
12
Io ( io:: Error ) ,
14
- RustfmtNotInstalled ,
15
- WalkDir ( walkdir:: Error ) ,
16
- IntellijSetupActive ,
17
13
Parse ( PathBuf , usize , String ) ,
18
14
CheckFailed ,
19
15
}
@@ -24,50 +20,22 @@ impl From<io::Error> for Error {
24
20
}
25
21
}
26
22
27
- impl From < walkdir:: Error > for Error {
28
- fn from ( error : walkdir:: Error ) -> Self {
29
- Self :: WalkDir ( error)
30
- }
31
- }
32
-
33
23
impl Error {
34
24
fn display ( & self ) {
35
25
match self {
36
26
Self :: CheckFailed => {
37
27
eprintln ! ( "Formatting check failed!\n Run `cargo dev fmt` to update." ) ;
38
28
} ,
39
- Self :: CommandFailed ( command, stderr) => {
40
- eprintln ! ( "error: command `{command}` failed!\n stderr: {stderr}" ) ;
41
- } ,
42
29
Self :: Io ( err) => {
43
30
eprintln ! ( "error: {err}" ) ;
44
31
} ,
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
- } ,
58
32
Self :: Parse ( path, line, msg) => {
59
33
eprintln ! ( "error parsing `{}:{line}`: {msg}" , path. display( ) ) ;
60
34
} ,
61
35
}
62
36
}
63
37
}
64
38
65
- struct FmtContext {
66
- check : bool ,
67
- verbose : bool ,
68
- rustfmt_path : String ,
69
- }
70
-
71
39
struct ClippyConf < ' a > {
72
40
name : & ' a str ,
73
41
attrs : & ' a str ,
@@ -257,155 +225,120 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
257
225
Ok ( ( ) )
258
226
}
259
227
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) ?;
228
+ fn run_rustfmt ( clippy : & ClippyInfo , update_mode : UpdateMode ) {
229
+ let mut rustfmt_path = String :: from_utf8 ( run_with_output (
230
+ "rustup which rustfmt" ,
231
+ Command :: new ( "rustup" ) . args ( [ "which" , "rustfmt" ] ) ,
232
+ ) )
233
+ . expect ( "invalid rustfmt path" ) ;
234
+ rustfmt_path. truncate ( rustfmt_path. trim_end ( ) . len ( ) ) ;
272
235
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 ( ) ) ?;
236
+ let mut cargo_path = String :: from_utf8 ( run_with_output (
237
+ "rustup which cargo" ,
238
+ Command :: new ( "rustup" ) . args ( [ "which" , "cargo" ] ) ,
239
+ ) )
240
+ . expect ( "invalid cargo path" ) ;
241
+ cargo_path. truncate ( cargo_path. trim_end ( ) . len ( ) ) ;
242
+
243
+ // Start all format jobs first before waiting on the results.
244
+ let mut children = Vec :: with_capacity ( 16 ) ;
245
+ for & path in & [
246
+ "." ,
247
+ "clippy_config" ,
248
+ "clippy_dev" ,
249
+ "clippy_lints" ,
250
+ "clippy_utils" ,
251
+ "rustc_tools_util" ,
252
+ "lintcheck" ,
253
+ ] {
254
+ let mut cmd = Command :: new ( & cargo_path) ;
255
+ cmd. current_dir ( clippy. path . join ( path) )
256
+ . args ( [ "fmt" ] )
257
+ . env ( "RUSTFMT" , & rustfmt_path)
258
+ . stdout ( Stdio :: null ( ) )
259
+ . stdin ( Stdio :: null ( ) )
260
+ . stderr ( Stdio :: piped ( ) ) ;
261
+ if update_mode. is_check ( ) {
262
+ cmd. arg ( "--check" ) ;
263
+ }
264
+ match cmd. spawn ( ) {
265
+ Ok ( x) => children. push ( ( "cargo fmt" , x) ) ,
266
+ Err ( ref e) => panic_action ( & e, ErrAction :: Run , "cargo fmt" . as_ref ( ) ) ,
267
+ }
268
+ }
277
269
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 ( ) )
270
+ run_with_args_split (
271
+ || {
272
+ let mut cmd = Command :: new ( & rustfmt_path) ;
273
+ if update_mode. is_check ( ) {
274
+ cmd. arg ( "--check" ) ;
293
275
}
294
- } )
295
- . chunks ( 250 ) ;
276
+ cmd. stdout ( Stdio :: null ( ) )
277
+ . stdin ( Stdio :: null ( ) )
278
+ . stderr ( Stdio :: piped ( ) )
279
+ . args ( [ "--config" , "show_parse_errors=false" ] ) ;
280
+ cmd
281
+ } ,
282
+ |cmd| match cmd. spawn ( ) {
283
+ Ok ( x) => children. push ( ( "rustfmt" , x) ) ,
284
+ Err ( ref e) => panic_action ( & e, ErrAction :: Run , "rustfmt" . as_ref ( ) ) ,
285
+ } ,
286
+ WalkDir :: new ( "tests" )
287
+ . into_iter ( )
288
+ . filter_entry ( |p| p. path ( ) . file_name ( ) . is_none_or ( |x| x != "skip_rustfmt" ) )
289
+ . filter_map ( |e| {
290
+ let e = e. expect ( "error reading `tests`" ) ;
291
+ e. path ( )
292
+ . as_os_str ( )
293
+ . as_encoded_bytes ( )
294
+ . ends_with ( b".rs" )
295
+ . then ( || e. into_path ( ) . into_os_string ( ) )
296
+ } ) ,
297
+ ) ;
296
298
297
- for chunk in & chunks {
298
- rustfmt ( context, chunk) ?;
299
+ for ( name, child) in & mut children {
300
+ match child. wait ( ) {
301
+ Ok ( status) => match ( update_mode, status. exit_ok ( ) ) {
302
+ ( UpdateMode :: Check | UpdateMode :: Change , Ok ( ( ) ) ) => { } ,
303
+ ( UpdateMode :: Check , Err ( _) ) => {
304
+ let mut s = String :: new ( ) ;
305
+ if let Some ( mut stderr) = child. stderr . take ( )
306
+ && stderr. read_to_string ( & mut s) . is_ok ( )
307
+ {
308
+ eprintln ! ( "{s}" ) ;
309
+ }
310
+ eprintln ! ( "Formatting check failed!\n Run `cargo dev fmt` to update." ) ;
311
+ process:: exit ( 1 ) ;
312
+ } ,
313
+ ( UpdateMode :: Change , Err ( e) ) => {
314
+ let mut s = String :: new ( ) ;
315
+ if let Some ( mut stderr) = child. stderr . take ( )
316
+ && stderr. read_to_string ( & mut s) . is_ok ( )
317
+ {
318
+ eprintln ! ( "{s}" ) ;
319
+ }
320
+ panic_action ( & e, ErrAction :: Run , name. as_ref ( ) ) ;
321
+ } ,
322
+ } ,
323
+ Err ( ref e) => panic_action ( e, ErrAction :: Run , name. as_ref ( ) ) ,
324
+ }
299
325
}
300
- Ok ( ( ) )
301
326
}
302
327
303
328
// 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 ) ;
329
+ pub fn run ( clippy : & ClippyInfo , update_mode : UpdateMode ) {
330
+ if clippy. has_intellij_hook {
331
+ eprintln ! (
332
+ "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n \
333
+ Not formatting because that would format the local repo as well!\n \
334
+ Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
335
+ ) ;
336
+ return ;
313
337
}
314
- let mut rustfmt_path = String :: from_utf8 ( output. stdout ) . expect ( "invalid rustfmt path" ) ;
315
- rustfmt_path. truncate ( rustfmt_path. trim_end ( ) . len ( ) ) ;
338
+ run_rustfmt ( clippy, update_mode) ;
316
339
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) ) {
340
+ if let Err ( e) = fmt_conf ( update_mode. is_check ( ) ) {
323
341
e. display ( ) ;
324
342
process:: exit ( 1 ) ;
325
343
}
326
344
}
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
- }
0 commit comments