@@ -17,14 +17,14 @@ use clap::ArgMatches;
1717use serde:: { Deserialize , Serialize } ;
1818use serde_json:: Value ;
1919
20- // use this to store the crates when interacting with the crates .toml file
20+ /// List of sources to check, loaded from a .toml file
2121#[ derive( Debug , Serialize , Deserialize ) ]
22- struct CrateList {
22+ struct SourceList {
2323 crates : HashMap < String , TomlCrate > ,
2424}
2525
26- // crate data we stored in the toml, can have multiple versions per crate
27- // A single TomlCrate is laster mapped to several CrateSources in that case
26+ /// A crate source stored inside the . toml
27+ /// will be translated into on one of the `CrateSource` variants
2828#[ derive( Debug , Serialize , Deserialize ) ]
2929struct TomlCrate {
3030 name : String ,
@@ -34,17 +34,16 @@ struct TomlCrate {
3434 path : Option < String > ,
3535}
3636
37- // represents an archive we download from crates.io, or a git repo, or a local repo
37+ /// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
38+ /// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
3839#[ derive( Debug , Serialize , Deserialize , Eq , Hash , PartialEq ) ]
3940enum CrateSource {
4041 CratesIo { name : String , version : String } ,
4142 Git { name : String , url : String , commit : String } ,
4243 Path { name : String , path : PathBuf } ,
4344}
4445
45- // represents the extracted sourcecode of a crate
46- // we actually don't need to special-case git repos here because it does not matter for clippy, yay!
47- // (clippy only needs a simple path)
46+ /// Represents the actual source code of a crate that we ran "cargo clippy" on
4847#[ derive( Debug ) ]
4948struct Crate {
5049 version : String ,
@@ -53,6 +52,7 @@ struct Crate {
5352 path : PathBuf ,
5453}
5554
55+ /// A single warning that clippy issued while checking a `Crate`
5656#[ derive( Debug ) ]
5757struct ClippyWarning {
5858 crate_name : String ,
@@ -62,7 +62,7 @@ struct ClippyWarning {
6262 column : String ,
6363 linttype : String ,
6464 message : String ,
65- ice : bool ,
65+ is_ice : bool ,
6666}
6767
6868impl std:: fmt:: Display for ClippyWarning {
@@ -76,6 +76,9 @@ impl std::fmt::Display for ClippyWarning {
7676}
7777
7878impl CrateSource {
79+ /// Makes the sources available on the disk for clippy to check.
80+ /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
81+ /// copies a local folder
7982 fn download_and_extract ( & self ) -> Crate {
8083 match self {
8184 CrateSource :: CratesIo { name, version } => {
@@ -122,19 +125,28 @@ impl CrateSource {
122125 // clone the repo if we have not done so
123126 if !repo_path. is_dir ( ) {
124127 println ! ( "Cloning {} and checking out {}" , url, commit) ;
125- Command :: new ( "git" )
128+ if ! Command :: new ( "git" )
126129 . arg ( "clone" )
127130 . arg ( url)
128131 . arg ( & repo_path)
129- . output ( )
130- . expect ( "Failed to clone git repo!" ) ;
132+ . status ( )
133+ . expect ( "Failed to clone git repo!" )
134+ . success ( )
135+ {
136+ eprintln ! ( "Failed to clone {} into {}" , url, repo_path. display( ) )
137+ }
131138 }
132139 // check out the commit/branch/whatever
133- Command :: new ( "git" )
140+ if ! Command :: new ( "git" )
134141 . arg ( "checkout" )
135142 . arg ( commit)
136- . output ( )
137- . expect ( "Failed to check out commit" ) ;
143+ . current_dir ( & repo_path)
144+ . status ( )
145+ . expect ( "Failed to check out commit" )
146+ . success ( )
147+ {
148+ eprintln ! ( "Failed to checkout {} of repo at {}" , commit, repo_path. display( ) )
149+ }
138150
139151 Crate {
140152 version : commit. clone ( ) ,
@@ -178,6 +190,8 @@ impl CrateSource {
178190}
179191
180192impl Crate {
193+ /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
194+ /// issued
181195 fn run_clippy_lints ( & self , cargo_clippy_path : & PathBuf ) -> Vec < ClippyWarning > {
182196 println ! ( "Linting {} {}..." , & self . name, & self . version) ;
183197 let cargo_clippy_path = std:: fs:: canonicalize ( cargo_clippy_path) . unwrap ( ) ;
@@ -211,21 +225,45 @@ impl Crate {
211225 let warnings: Vec < ClippyWarning > = output_lines
212226 . into_iter ( )
213227 // get all clippy warnings and ICEs
214- . filter ( |line| line . contains ( "clippy::" ) || line. contains ( "internal compiler error: " ) )
228+ . filter ( |line| filter_clippy_warnings ( & line) )
215229 . map ( |json_msg| parse_json_message ( json_msg, & self ) )
216230 . collect ( ) ;
217231 warnings
218232 }
219233}
220234
235+ /// takes a single json-formatted clippy warnings and returns true (we are interested in that line)
236+ /// or false (we aren't)
237+ fn filter_clippy_warnings ( line : & str ) -> bool {
238+ // we want to collect ICEs because clippy might have crashed.
239+ // these are summarized later
240+ if line. contains ( "internal compiler error: " ) {
241+ return true ;
242+ }
243+ // in general, we want all clippy warnings
244+ // however due to some kind of bug, sometimes there are absolute paths
245+ // to libcore files inside the message
246+ // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508)
247+
248+ // filter out these message to avoid unnecessary noise in the logs
249+ if line. contains ( "clippy::" )
250+ && !( line. contains ( "could not read cargo metadata" )
251+ || ( line. contains ( ".rustup" ) && line. contains ( "toolchains" ) ) )
252+ {
253+ return true ;
254+ }
255+ false
256+ }
257+
258+ /// Builds clippy inside the repo to make sure we have a clippy executable we can use.
221259fn build_clippy ( ) {
222260 Command :: new ( "cargo" )
223261 . arg ( "build" )
224262 . output ( )
225263 . expect ( "Failed to build clippy!" ) ;
226264}
227265
228- // get a list of CrateSources we want to check from a "lintcheck_crates.toml" file.
266+ /// Read a `toml` file and return a list of ` CrateSources` that we want to check with clippy
229267fn read_crates ( toml_path : Option < & str > ) -> ( String , Vec < CrateSource > ) {
230268 let toml_path = PathBuf :: from (
231269 env:: var ( "LINTCHECK_TOML" ) . unwrap_or ( toml_path. unwrap_or ( "clippy_dev/lintcheck_crates.toml" ) . to_string ( ) ) ,
@@ -234,7 +272,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
234272 let toml_filename = toml_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
235273 let toml_content: String =
236274 std:: fs:: read_to_string ( & toml_path) . unwrap_or_else ( |_| panic ! ( "Failed to read {}" , toml_path. display( ) ) ) ;
237- let crate_list: CrateList =
275+ let crate_list: SourceList =
238276 toml:: from_str ( & toml_content) . unwrap_or_else ( |e| panic ! ( "Failed to parse {}: \n {}" , toml_path. display( ) , e) ) ;
239277 // parse the hashmap of the toml file into a list of crates
240278 let tomlcrates: Vec < TomlCrate > = crate_list
@@ -288,7 +326,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
288326 ( toml_filename, crate_sources)
289327}
290328
291- // extract interesting data from a json lint message
329+ /// Parse the json output of clippy and return a `ClippyWarning`
292330fn parse_json_message ( json_message : & str , krate : & Crate ) -> ClippyWarning {
293331 let jmsg: Value = serde_json:: from_str ( & json_message) . unwrap_or_else ( |e| panic ! ( "Failed to parse json:\n {:?}" , e) ) ;
294332
@@ -309,11 +347,31 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
309347 . into ( ) ,
310348 linttype : jmsg[ "message" ] [ "code" ] [ "code" ] . to_string ( ) . trim_matches ( '"' ) . into ( ) ,
311349 message : jmsg[ "message" ] [ "message" ] . to_string ( ) . trim_matches ( '"' ) . into ( ) ,
312- ice : json_message. contains ( "internal compiler error: " ) ,
350+ is_ice : json_message. contains ( "internal compiler error: " ) ,
313351 }
314352}
315353
316- // the main fn
354+ /// Generate a short list of occuring lints-types and their count
355+ fn gather_stats ( clippy_warnings : & [ ClippyWarning ] ) -> String {
356+ // count lint type occurrences
357+ let mut counter: HashMap < & String , usize > = HashMap :: new ( ) ;
358+ clippy_warnings
359+ . iter ( )
360+ . for_each ( |wrn| * counter. entry ( & wrn. linttype ) . or_insert ( 0 ) += 1 ) ;
361+
362+ // collect into a tupled list for sorting
363+ let mut stats: Vec < ( & & String , & usize ) > = counter. iter ( ) . map ( |( lint, count) | ( lint, count) ) . collect ( ) ;
364+ // sort by "000{count} {clippy::lintname}"
365+ // to not have a lint with 200 and 2 warnings take the same spot
366+ stats. sort_by_key ( |( lint, count) | format ! ( "{:0>4}, {}" , count, lint) ) ;
367+
368+ stats
369+ . iter ( )
370+ . map ( |( lint, count) | format ! ( "{} {}\n " , lint, count) )
371+ . collect :: < String > ( )
372+ }
373+
374+ /// lintchecks `main()` function
317375pub fn run ( clap_config : & ArgMatches ) {
318376 let cargo_clippy_path: PathBuf = PathBuf :: from ( "target/debug/cargo-clippy" ) ;
319377
@@ -374,32 +432,16 @@ pub fn run(clap_config: &ArgMatches) {
374432 . collect ( )
375433 } ;
376434
377- // generate some stats:
435+ // generate some stats
436+ let stats_formatted = gather_stats ( & clippy_warnings) ;
378437
379438 // grab crashes/ICEs, save the crate name and the ice message
380439 let ices: Vec < ( & String , & String ) > = clippy_warnings
381440 . iter ( )
382- . filter ( |warning| warning. ice )
441+ . filter ( |warning| warning. is_ice )
383442 . map ( |w| ( & w. crate_name , & w. message ) )
384443 . collect ( ) ;
385444
386- // count lint type occurrences
387- let mut counter: HashMap < & String , usize > = HashMap :: new ( ) ;
388- clippy_warnings
389- . iter ( )
390- . for_each ( |wrn| * counter. entry ( & wrn. linttype ) . or_insert ( 0 ) += 1 ) ;
391-
392- // collect into a tupled list for sorting
393- let mut stats: Vec < ( & & String , & usize ) > = counter. iter ( ) . map ( |( lint, count) | ( lint, count) ) . collect ( ) ;
394- // sort by "000{count} {clippy::lintname}"
395- // to not have a lint with 200 and 2 warnings take the same spot
396- stats. sort_by_key ( |( lint, count) | format ! ( "{:0>4}, {}" , count, lint) ) ;
397-
398- let stats_formatted: String = stats
399- . iter ( )
400- . map ( |( lint, count) | format ! ( "{} {}\n " , lint, count) )
401- . collect :: < String > ( ) ;
402-
403445 let mut all_msgs: Vec < String > = clippy_warnings. iter ( ) . map ( |warning| warning. to_string ( ) ) . collect ( ) ;
404446 all_msgs. sort ( ) ;
405447 all_msgs. push ( "\n \n \n \n Stats\n \n " . into ( ) ) ;
0 commit comments