@@ -336,8 +336,7 @@ fn build_clippy() {
336336}
337337
338338/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy
339- fn read_crates ( toml_path : Option < & str > ) -> ( String , Vec < CrateSource > ) {
340- let toml_path = lintcheck_config_toml ( toml_path) ;
339+ fn read_crates ( toml_path : & PathBuf ) -> ( String , Vec < CrateSource > ) {
341340 // save it so that we can use the name of the sources.toml as name for the logfile later.
342341 let toml_filename = toml_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
343342 let toml_content: String =
@@ -428,7 +427,7 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
428427}
429428
430429/// Generate a short list of occuring lints-types and their count
431- fn gather_stats ( clippy_warnings : & [ ClippyWarning ] ) -> String {
430+ fn gather_stats ( clippy_warnings : & [ ClippyWarning ] ) -> ( String , HashMap < & String , usize > ) {
432431 // count lint type occurrences
433432 let mut counter: HashMap < & String , usize > = HashMap :: new ( ) ;
434433 clippy_warnings
@@ -441,15 +440,17 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> String {
441440 // to not have a lint with 200 and 2 warnings take the same spot
442441 stats. sort_by_key ( |( lint, count) | format ! ( "{:0>4}, {}" , count, lint) ) ;
443442
444- stats
443+ let stats_string = stats
445444 . iter ( )
446445 . map ( |( lint, count) | format ! ( "{} {}\n " , lint, count) )
447- . collect :: < String > ( )
446+ . collect :: < String > ( ) ;
447+
448+ ( stats_string, counter)
448449}
449450
450451/// check if the latest modification of the logfile is older than the modification date of the
451452/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
452- fn lintcheck_needs_rerun ( toml_path : Option < & str > ) -> bool {
453+ fn lintcheck_needs_rerun ( toml_path : & PathBuf ) -> bool {
453454 let clippy_modified: std:: time:: SystemTime = {
454455 let mut times = [ "target/debug/clippy-driver" , "target/debug/cargo-clippy" ]
455456 . iter ( )
@@ -459,17 +460,18 @@ fn lintcheck_needs_rerun(toml_path: Option<&str>) -> bool {
459460 . modified ( )
460461 . expect ( "failed to get modification date" )
461462 } ) ;
462- // the lates modification of either of the binaries
463- std:: cmp:: max ( times. next ( ) . unwrap ( ) , times. next ( ) . unwrap ( ) )
463+ // the oldest modification of either of the binaries
464+ std:: cmp:: min ( times. next ( ) . unwrap ( ) , times. next ( ) . unwrap ( ) )
464465 } ;
465466
466- let logs_modified: std:: time:: SystemTime = std:: fs:: metadata ( lintcheck_config_toml ( toml_path) )
467+ let logs_modified: std:: time:: SystemTime = std:: fs:: metadata ( toml_path)
467468 . expect ( "failed to get metadata of file" )
468469 . modified ( )
469470 . expect ( "failed to get modification date" ) ;
470471
471- // if clippys modification time is bigger (older) than the logs mod time, we need to rerun lintcheck
472- clippy_modified > logs_modified
472+ // if clippys modification time is smaller (older) than the logs mod time, we need to rerun
473+ // lintcheck
474+ clippy_modified < logs_modified
473475}
474476
475477/// lintchecks `main()` function
@@ -478,11 +480,12 @@ pub fn run(clap_config: &ArgMatches) {
478480 build_clippy ( ) ;
479481 println ! ( "Done compiling" ) ;
480482
481- let clap_toml_path = clap_config. value_of ( "crates-toml" ) ;
483+ let clap_toml_path: Option < & str > = clap_config. value_of ( "crates-toml" ) ;
484+ let toml_path: PathBuf = lintcheck_config_toml ( clap_toml_path) ;
482485
483486 // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
484487 // refresh the logs
485- if lintcheck_needs_rerun ( clap_toml_path ) {
488+ if lintcheck_needs_rerun ( & toml_path ) {
486489 let shared_target_dir = "target/lintcheck/shared_target_dir" ;
487490 match std:: fs:: metadata ( & shared_target_dir) {
488491 Ok ( metadata) => {
@@ -517,7 +520,9 @@ pub fn run(clap_config: &ArgMatches) {
517520 // download and extract the crates, then run clippy on them and collect clippys warnings
518521 // flatten into one big list of warnings
519522
520- let ( filename, crates) = read_crates ( clap_toml_path) ;
523+ let ( filename, crates) = read_crates ( & toml_path) ;
524+ let file = format ! ( "lintcheck-logs/{}_logs.txt" , filename) ;
525+ let old_stats = read_stats_from_file ( & file) ;
521526
522527 let clippy_warnings: Vec < ClippyWarning > = if let Some ( only_one_crate) = clap_config. value_of ( "only" ) {
523528 // if we don't have the specified crate in the .toml, throw an error
@@ -586,7 +591,7 @@ pub fn run(clap_config: &ArgMatches) {
586591 } ;
587592
588593 // generate some stats
589- let stats_formatted = gather_stats ( & clippy_warnings) ;
594+ let ( stats_formatted, new_stats ) = gather_stats ( & clippy_warnings) ;
590595
591596 // grab crashes/ICEs, save the crate name and the ice message
592597 let ices: Vec < ( & String , & String ) > = clippy_warnings
@@ -597,7 +602,7 @@ pub fn run(clap_config: &ArgMatches) {
597602
598603 let mut all_msgs: Vec < String > = clippy_warnings. iter ( ) . map ( |warning| warning. to_string ( ) ) . collect ( ) ;
599604 all_msgs. sort ( ) ;
600- all_msgs. push ( "\n \n \n \n Stats\n \n " . into ( ) ) ;
605+ all_msgs. push ( "\n \n \n \n Stats: \n " . into ( ) ) ;
601606 all_msgs. push ( stats_formatted) ;
602607
603608 // save the text into lintcheck-logs/logs.txt
@@ -607,7 +612,84 @@ pub fn run(clap_config: &ArgMatches) {
607612 ices. iter ( )
608613 . for_each ( |( cratename, msg) | text. push_str ( & format ! ( "{}: '{}'" , cratename, msg) ) ) ;
609614
610- let file = format ! ( "lintcheck-logs/{}_logs.txt" , filename) ;
611615 println ! ( "Writing logs to {}" , file) ;
612- write ( file, text) . unwrap ( ) ;
616+ write ( & file, text) . unwrap ( ) ;
617+
618+ print_stats ( old_stats, new_stats) ;
619+ }
620+
621+ /// read the previous stats from the lintcheck-log file
622+ fn read_stats_from_file ( file_path : & String ) -> HashMap < String , usize > {
623+ let file_path = PathBuf :: from ( file_path) ;
624+ let file_content: String = match std:: fs:: read_to_string ( file_path) . ok ( ) {
625+ Some ( content) => content,
626+ None => {
627+ eprintln ! ( "RETURND" ) ;
628+ return HashMap :: new ( ) ;
629+ } ,
630+ } ;
631+
632+ let lines: Vec < String > = file_content. lines ( ) . map ( |l| l. to_string ( ) ) . collect ( ) ;
633+
634+ // search for the beginning "Stats:" and the end "ICEs:" of the section we want
635+ let start = lines. iter ( ) . position ( |line| line == "Stats:" ) . unwrap ( ) ;
636+ let end = lines. iter ( ) . position ( |line| line == "ICEs:" ) . unwrap ( ) ;
637+
638+ let stats_lines = & lines[ start + 1 ..=end - 1 ] ;
639+
640+ stats_lines
641+ . into_iter ( )
642+ . map ( |line| {
643+ let mut spl = line. split ( " " ) . into_iter ( ) ;
644+ (
645+ spl. next ( ) . unwrap ( ) . to_string ( ) ,
646+ spl. next ( ) . unwrap ( ) . parse :: < usize > ( ) . unwrap ( ) ,
647+ )
648+ } )
649+ . collect :: < HashMap < String , usize > > ( )
650+ }
651+
652+ /// print how lint counts changed between runs
653+ fn print_stats ( old_stats : HashMap < String , usize > , new_stats : HashMap < & String , usize > ) {
654+ let same_in_both_hashmaps = old_stats
655+ . iter ( )
656+ . filter ( |( old_key, old_val) | new_stats. get :: < & String > ( & old_key) == Some ( old_val) )
657+ . map ( |( k, v) | ( k. to_string ( ) , * v) )
658+ . collect :: < Vec < ( String , usize ) > > ( ) ;
659+
660+ let mut old_stats_deduped = old_stats;
661+ let mut new_stats_deduped = new_stats;
662+
663+ // remove duplicates from both hashmaps
664+ same_in_both_hashmaps. iter ( ) . for_each ( |( k, v) | {
665+ assert ! ( old_stats_deduped. remove( k) == Some ( * v) ) ;
666+ assert ! ( new_stats_deduped. remove( k) == Some ( * v) ) ;
667+ } ) ;
668+
669+ println ! ( "\n Stats:" ) ;
670+
671+ // list all new counts (key is in new stats but not in old stats)
672+ new_stats_deduped
673+ . iter ( )
674+ . filter ( |( new_key, _) | old_stats_deduped. get :: < str > ( & new_key) . is_none ( ) )
675+ . for_each ( |( new_key, new_value) | {
676+ println ! ( "{} 0 => {}" , new_key, new_value) ;
677+ } ) ;
678+
679+ // list all changed counts (key is in both maps but value differs)
680+ new_stats_deduped
681+ . iter ( )
682+ . filter ( |( new_key, _new_val) | old_stats_deduped. get :: < str > ( & new_key) . is_some ( ) )
683+ . for_each ( |( new_key, new_val) | {
684+ let old_val = old_stats_deduped. get :: < str > ( & new_key) . unwrap ( ) ;
685+ println ! ( "{} {} => {}" , new_key, old_val, new_val) ;
686+ } ) ;
687+
688+ // list all gone counts (key is in old status but not in new stats)
689+ old_stats_deduped
690+ . iter ( )
691+ . filter ( |( old_key, _) | new_stats_deduped. get :: < & String > ( & old_key) . is_none ( ) )
692+ . for_each ( |( old_key, old_value) | {
693+ println ! ( "{} {} => 0" , old_key, old_value) ;
694+ } ) ;
613695}
0 commit comments