@@ -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 ( clap_toml_path : Option < & str > ) -> ( String , Vec < CrateSource > ) {
340- let toml_path = lintcheck_config_toml ( clap_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+ dbg ! ( clippy_modified < logs_modified)
473475}
474476
475477/// lintchecks `main()` function
@@ -479,11 +481,11 @@ pub fn run(clap_config: &ArgMatches) {
479481 println ! ( "Done compiling" ) ;
480482
481483 let clap_toml_path: Option < & str > = clap_config. value_of ( "crates-toml" ) ;
482- let toml_path = lintcheck_config_toml ( clap_toml_path) ;
484+ let toml_path: PathBuf = lintcheck_config_toml ( clap_toml_path) ;
483485
484486 // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
485487 // refresh the logs
486- if lintcheck_needs_rerun ( clap_toml_path ) {
488+ if dbg ! ( lintcheck_needs_rerun( & toml_path ) ) {
487489 let shared_target_dir = "target/lintcheck/shared_target_dir" ;
488490 match std:: fs:: metadata ( & shared_target_dir) {
489491 Ok ( metadata) => {
@@ -518,7 +520,9 @@ pub fn run(clap_config: &ArgMatches) {
518520 // download and extract the crates, then run clippy on them and collect clippys warnings
519521 // flatten into one big list of warnings
520522
521- 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) ;
522526
523527 let clippy_warnings: Vec < ClippyWarning > = if let Some ( only_one_crate) = clap_config. value_of ( "only" ) {
524528 // if we don't have the specified crate in the .toml, throw an error
@@ -587,7 +591,7 @@ pub fn run(clap_config: &ArgMatches) {
587591 } ;
588592
589593 // generate some stats
590- let stats_formatted = gather_stats ( & clippy_warnings) ;
594+ let ( stats_formatted, new_stats ) = gather_stats ( & clippy_warnings) ;
591595
592596 // grab crashes/ICEs, save the crate name and the ice message
593597 let ices: Vec < ( & String , & String ) > = clippy_warnings
@@ -598,7 +602,7 @@ pub fn run(clap_config: &ArgMatches) {
598602
599603 let mut all_msgs: Vec < String > = clippy_warnings. iter ( ) . map ( |warning| warning. to_string ( ) ) . collect ( ) ;
600604 all_msgs. sort ( ) ;
601- all_msgs. push ( "\n \n \n \n Stats\n \n " . into ( ) ) ;
605+ all_msgs. push ( "\n \n \n \n Stats: \n " . into ( ) ) ;
602606 all_msgs. push ( stats_formatted) ;
603607
604608 // save the text into lintcheck-logs/logs.txt
@@ -608,7 +612,85 @@ pub fn run(clap_config: &ArgMatches) {
608612 ices. iter ( )
609613 . for_each ( |( cratename, msg) | text. push_str ( & format ! ( "{}: '{}'" , cratename, msg) ) ) ;
610614
611- let file = format ! ( "lintcheck-logs/{}_logs.txt" , filename) ;
612615 println ! ( "Writing logs to {}" , file) ;
613- 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+ dbg ! ( & file_path) ;
625+ let file_content: String = match std:: fs:: read_to_string ( file_path) . ok ( ) {
626+ Some ( content) => content,
627+ None => {
628+ eprintln ! ( "RETURND" ) ;
629+ return HashMap :: new ( ) ;
630+ } ,
631+ } ;
632+
633+ let lines: Vec < String > = file_content. lines ( ) . map ( |l| l. to_string ( ) ) . collect ( ) ;
634+
635+ // search for the beginning "Stats:" and the end "ICEs:" of the section we want
636+ let start = lines. iter ( ) . position ( |line| line == "Stats:" ) . unwrap ( ) ;
637+ let end = lines. iter ( ) . position ( |line| line == "ICEs:" ) . unwrap ( ) ;
638+
639+ let stats_lines = & lines[ start + 1 ..=end - 1 ] ;
640+
641+ stats_lines
642+ . into_iter ( )
643+ . map ( |line| {
644+ let mut spl = line. split ( " " ) . into_iter ( ) ;
645+ (
646+ spl. next ( ) . unwrap ( ) . to_string ( ) ,
647+ spl. next ( ) . unwrap ( ) . parse :: < usize > ( ) . unwrap ( ) ,
648+ )
649+ } )
650+ . collect :: < HashMap < String , usize > > ( )
651+ }
652+
653+ /// print how lint counts changed between runs
654+ fn print_stats ( old_stats : HashMap < String , usize > , new_stats : HashMap < & String , usize > ) {
655+ let same_in_both_hashmaps = old_stats
656+ . iter ( )
657+ . filter ( |( old_key, old_val) | new_stats. get :: < & String > ( & old_key) == Some ( old_val) )
658+ . map ( |( k, v) | ( k. to_string ( ) , * v) )
659+ . collect :: < Vec < ( String , usize ) > > ( ) ;
660+
661+ let mut old_stats_deduped = old_stats;
662+ let mut new_stats_deduped = new_stats;
663+
664+ // remove duplicates from both hashmaps
665+ same_in_both_hashmaps. iter ( ) . for_each ( |( k, v) | {
666+ assert ! ( old_stats_deduped. remove( k) == Some ( * v) ) ;
667+ assert ! ( new_stats_deduped. remove( k) == Some ( * v) ) ;
668+ } ) ;
669+
670+ println ! ( "\n Stats:" ) ;
671+
672+ // list all new counts (key is in new stats but not in old stats)
673+ new_stats_deduped
674+ . iter ( )
675+ . filter ( |( new_key, _) | old_stats_deduped. get :: < str > ( & new_key) . is_none ( ) )
676+ . for_each ( |( new_key, new_value) | {
677+ println ! ( "{} 0 => {}" , new_key, new_value) ;
678+ } ) ;
679+
680+ // list all changed counts (key is in both maps but value differs)
681+ new_stats_deduped
682+ . iter ( )
683+ . filter ( |( new_key, _new_val) | old_stats_deduped. get :: < str > ( & new_key) . is_some ( ) )
684+ . for_each ( |( new_key, new_val) | {
685+ let old_val = old_stats_deduped. get :: < str > ( & new_key) . unwrap ( ) ;
686+ println ! ( "{} {} => {}" , new_key, old_val, new_val) ;
687+ } ) ;
688+
689+ // list all gone counts (key is in old status but not in new stats)
690+ old_stats_deduped
691+ . iter ( )
692+ . filter ( |( old_key, _) | new_stats_deduped. get :: < & String > ( & old_key) . is_none ( ) )
693+ . for_each ( |( old_key, old_value) | {
694+ println ! ( "{} {} => 0" , old_key, old_value) ;
695+ } ) ;
614696}
0 commit comments