33
44use std:: {
55 env:: { consts:: OS , current_dir} ,
6- fs,
76 path:: PathBuf ,
8- process:: Command ,
9- sync:: { Arc , Mutex , MutexGuard } ,
7+ sync:: { Arc , Mutex } ,
108} ;
119
1210// non-std crates
13- use anyhow:: { Context , Result } ;
11+ use anyhow:: { anyhow , Context , Result } ;
1412use regex:: Regex ;
1513use serde:: Deserialize ;
14+ use tokio:: { fs, process:: Command } ;
1615
1716// project-specific modules/crates
1817use super :: MakeSuggestions ;
@@ -249,63 +248,73 @@ pub fn tally_tidy_advice(files: &[Arc<Mutex<FileObj>>]) -> u64 {
249248}
250249
251250/// Run clang-tidy, then parse and return it's output.
252- pub fn run_clang_tidy (
253- file : & mut MutexGuard < FileObj > ,
251+ pub async fn run_clang_tidy (
252+ file : & Arc < Mutex < FileObj > > ,
254253 clang_params : & ClangParams ,
255254) -> Result < Vec < ( log:: Level , std:: string:: String ) > > {
256- let mut cmd = Command :: new ( clang_params. clang_tidy_command . as_ref ( ) . unwrap ( ) ) ;
257255 let mut logs = vec ! [ ] ;
258- if !clang_params. tidy_checks . is_empty ( ) {
259- cmd. args ( [ "-checks" , & clang_params. tidy_checks ] ) ;
260- }
261- if let Some ( db) = & clang_params. database {
262- cmd. args ( [ "-p" , & db. to_string_lossy ( ) ] ) ;
263- }
264- for arg in & clang_params. extra_args {
265- cmd. args ( [ "--extra-arg" , format ! ( "\" {}\" " , arg) . as_str ( ) ] ) ;
266- }
267- let file_name = file. name . to_string_lossy ( ) . to_string ( ) ;
268- if clang_params. lines_changed_only != LinesChangedOnly :: Off {
269- let ranges = file. get_ranges ( & clang_params. lines_changed_only ) ;
270- if !ranges. is_empty ( ) {
271- let filter = format ! (
272- "[{{\" name\" :{:?},\" lines\" :{:?}}}]" ,
273- & file_name. replace( '/' , if OS == "windows" { "\\ " } else { "/" } ) ,
274- ranges
275- . iter( )
276- . map( |r| [ r. start( ) , r. end( ) ] )
277- . collect:: <Vec <_>>( )
278- ) ;
279- cmd. args ( [ "--line-filter" , filter. as_str ( ) ] ) ;
256+ let ( file_name, mut args) = {
257+ let mut args = vec ! [ ] ;
258+ let file = file
259+ . lock ( )
260+ . map_err ( |e| anyhow ! ( "Failed to lock mutex: {e:?}" ) ) ?;
261+ let file_name = file. name . to_string_lossy ( ) . to_string ( ) ;
262+ if !clang_params. tidy_checks . is_empty ( ) {
263+ args. extend ( [ "-checks" . to_string ( ) , clang_params. tidy_checks . to_owned ( ) ] ) ;
280264 }
281- }
265+ if let Some ( db) = & clang_params. database {
266+ args. extend ( [ "-p" . to_string ( ) , db. to_string_lossy ( ) . to_string ( ) ] ) ;
267+ }
268+ for arg in & clang_params. extra_args {
269+ args. extend ( [ "--extra-arg" . to_string ( ) , format ! ( "\" {}\" " , arg) ] ) ;
270+ }
271+ if clang_params. lines_changed_only != LinesChangedOnly :: Off {
272+ let ranges = file. get_ranges ( & clang_params. lines_changed_only ) ;
273+ if !ranges. is_empty ( ) {
274+ let filter = format ! (
275+ "[{{\" name\" :{:?},\" lines\" :{:?}}}]" ,
276+ & file_name. replace( '/' , if OS == "windows" { "\\ " } else { "/" } ) ,
277+ ranges
278+ . iter( )
279+ . map( |r| [ r. start( ) , r. end( ) ] )
280+ . collect:: <Vec <_>>( )
281+ ) ;
282+ args. extend ( [ "--line-filter" . to_string ( ) , filter] ) ;
283+ }
284+ }
285+ ( file_name, args)
286+ } ;
282287 let original_content = if !clang_params. tidy_review {
283288 None
284289 } else {
285- cmd . arg ( "--fix-errors" ) ;
286- Some ( fs:: read_to_string ( & file . name ) . with_context ( || {
290+ args . push ( "--fix-errors" . to_string ( ) ) ;
291+ Some ( fs:: read_to_string ( & file_name ) . await . with_context ( || {
287292 format ! (
288293 "Failed to cache file's original content before applying clang-tidy changes: {}" ,
289294 file_name. clone( )
290295 )
291296 } ) ?)
292297 } ;
293298 if !clang_params. style . is_empty ( ) {
294- cmd . args ( [ "--format-style" , clang_params. style . as_str ( ) ] ) ;
299+ args. extend ( [ "--format-style" . to_string ( ) , clang_params. style . to_owned ( ) ] ) ;
295300 }
296- cmd. arg ( file. name . to_string_lossy ( ) . as_ref ( ) ) ;
301+ args. push ( file_name. clone ( ) ) ;
302+ let program = clang_params. clang_tidy_command . as_ref ( ) . unwrap ( ) ;
303+ let mut cmd = Command :: new ( program) ;
304+ cmd. args ( & args) ;
297305 logs. push ( (
298306 log:: Level :: Info ,
299307 format ! (
300308 "Running \" {} {}\" " ,
301- cmd. get_program( ) . to_string_lossy( ) ,
302- cmd. get_args( )
303- . map( |x| x. to_string_lossy( ) )
304- . collect:: <Vec <_>>( )
305- . join( " " )
309+ program. to_string_lossy( ) ,
310+ args. join( " " )
306311 ) ,
307312 ) ) ;
308- let output = cmd. output ( ) . unwrap ( ) ;
313+ // ok to unwrap()
314+ let output = cmd
315+ . output ( )
316+ . await
317+ . with_context ( || format ! ( "Failed to run clang-tidy on file: {}" , file_name. clone( ) ) ) ?;
309318 logs. push ( (
310319 log:: Level :: Debug ,
311320 format ! (
@@ -322,22 +331,25 @@ pub fn run_clang_tidy(
322331 ) ,
323332 ) ) ;
324333 }
325- file. tidy_advice = Some ( parse_tidy_output (
326- & output. stdout ,
327- & clang_params. database_json ,
328- ) ?) ;
334+ let mut tidy_advice = parse_tidy_output ( & output. stdout , & clang_params. database_json ) ?;
329335 if clang_params. tidy_review {
330- if let Some ( tidy_advice) = & mut file. tidy_advice {
331- // cache file changes in a buffer and restore the original contents for further analysis
332- tidy_advice. patched =
333- Some ( fs:: read ( & file_name) . with_context ( || {
334- format ! ( "Failed to read changes from clang-tidy: {file_name}" )
335- } ) ?) ;
336- }
336+ // cache file changes in a buffer and restore the original contents for further analysis
337+ tidy_advice. patched = Some (
338+ fs:: read ( & file_name)
339+ . await
340+ . with_context ( || format ! ( "Failed to read changes from clang-tidy: {file_name}" ) ) ?,
341+ ) ;
337342 // original_content is guaranteed to be Some() value at this point
338343 fs:: write ( & file_name, original_content. unwrap ( ) )
344+ . await
339345 . with_context ( || format ! ( "Failed to restore file's original content: {file_name}" ) ) ?;
340346 }
347+ {
348+ let mut file = file
349+ . lock ( )
350+ . map_err ( |e| anyhow ! ( "Failed to lock mutex: {e:?}" ) ) ?;
351+ file. tidy_advice = Some ( tidy_advice) ;
352+ }
341353 Ok ( logs)
342354}
343355
@@ -420,8 +432,8 @@ mod test {
420432 )
421433 }
422434
423- #[ test]
424- fn use_extra_args ( ) {
435+ #[ tokio :: test]
436+ async fn use_extra_args ( ) {
425437 let exe_path = ClangTool :: ClangTidy
426438 . get_exe_path (
427439 & RequestedVersion :: from_str (
@@ -431,7 +443,7 @@ mod test {
431443 )
432444 . unwrap ( ) ;
433445 let file = FileObj :: new ( PathBuf :: from ( "tests/demo/demo.cpp" ) ) ;
434- let arc_ref = Arc :: new ( Mutex :: new ( file) ) ;
446+ let arc_file = Arc :: new ( Mutex :: new ( file) ) ;
435447 let extra_args = vec ! [ "-std=c++17" . to_string( ) , "-Wall" . to_string( ) ] ;
436448 let clang_params = ClangParams {
437449 style : "" . to_string ( ) ,
@@ -447,8 +459,8 @@ mod test {
447459 clang_tidy_command : Some ( exe_path) ,
448460 clang_format_command : None ,
449461 } ;
450- let mut file_lock = arc_ref . lock ( ) . unwrap ( ) ;
451- let logs = run_clang_tidy ( & mut file_lock , & clang_params )
462+ let logs = run_clang_tidy ( & arc_file , & clang_params )
463+ . await
452464 . unwrap ( )
453465 . into_iter ( )
454466 . filter_map ( |( _lvl, msg) | {
0 commit comments