@@ -17,14 +17,14 @@ use clap::ArgMatches;
17
17
use serde:: { Deserialize , Serialize } ;
18
18
use serde_json:: Value ;
19
19
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
21
21
#[ derive( Debug , Serialize , Deserialize ) ]
22
- struct CrateList {
22
+ struct SourceList {
23
23
crates : HashMap < String , TomlCrate > ,
24
24
}
25
25
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
28
28
#[ derive( Debug , Serialize , Deserialize ) ]
29
29
struct TomlCrate {
30
30
name : String ,
@@ -34,17 +34,16 @@ struct TomlCrate {
34
34
path : Option < String > ,
35
35
}
36
36
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`
38
39
#[ derive( Debug , Serialize , Deserialize , Eq , Hash , PartialEq ) ]
39
40
enum CrateSource {
40
41
CratesIo { name : String , version : String } ,
41
42
Git { name : String , url : String , commit : String } ,
42
43
Path { name : String , path : PathBuf } ,
43
44
}
44
45
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
48
47
#[ derive( Debug ) ]
49
48
struct Crate {
50
49
version : String ,
@@ -53,6 +52,7 @@ struct Crate {
53
52
path : PathBuf ,
54
53
}
55
54
55
+ /// A single warning that clippy issued while checking a `Crate`
56
56
#[ derive( Debug ) ]
57
57
struct ClippyWarning {
58
58
crate_name : String ,
@@ -62,7 +62,7 @@ struct ClippyWarning {
62
62
column : String ,
63
63
linttype : String ,
64
64
message : String ,
65
- ice : bool ,
65
+ is_ice : bool ,
66
66
}
67
67
68
68
impl std:: fmt:: Display for ClippyWarning {
@@ -76,6 +76,9 @@ impl std::fmt::Display for ClippyWarning {
76
76
}
77
77
78
78
impl 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
79
82
fn download_and_extract ( & self ) -> Crate {
80
83
match self {
81
84
CrateSource :: CratesIo { name, version } => {
@@ -122,19 +125,28 @@ impl CrateSource {
122
125
// clone the repo if we have not done so
123
126
if !repo_path. is_dir ( ) {
124
127
println ! ( "Cloning {} and checking out {}" , url, commit) ;
125
- Command :: new ( "git" )
128
+ if ! Command :: new ( "git" )
126
129
. arg ( "clone" )
127
130
. arg ( url)
128
131
. 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
+ }
131
138
}
132
139
// check out the commit/branch/whatever
133
- Command :: new ( "git" )
140
+ if ! Command :: new ( "git" )
134
141
. arg ( "checkout" )
135
142
. 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
+ }
138
150
139
151
Crate {
140
152
version : commit. clone ( ) ,
@@ -178,6 +190,8 @@ impl CrateSource {
178
190
}
179
191
180
192
impl Crate {
193
+ /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
194
+ /// issued
181
195
fn run_clippy_lints ( & self , cargo_clippy_path : & PathBuf ) -> Vec < ClippyWarning > {
182
196
println ! ( "Linting {} {}..." , & self . name, & self . version) ;
183
197
let cargo_clippy_path = std:: fs:: canonicalize ( cargo_clippy_path) . unwrap ( ) ;
@@ -211,21 +225,45 @@ impl Crate {
211
225
let warnings: Vec < ClippyWarning > = output_lines
212
226
. into_iter ( )
213
227
// get all clippy warnings and ICEs
214
- . filter ( |line| line . contains ( "clippy::" ) || line. contains ( "internal compiler error: " ) )
228
+ . filter ( |line| filter_clippy_warnings ( & line) )
215
229
. map ( |json_msg| parse_json_message ( json_msg, & self ) )
216
230
. collect ( ) ;
217
231
warnings
218
232
}
219
233
}
220
234
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.
221
259
fn build_clippy ( ) {
222
260
Command :: new ( "cargo" )
223
261
. arg ( "build" )
224
262
. output ( )
225
263
. expect ( "Failed to build clippy!" ) ;
226
264
}
227
265
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
229
267
fn read_crates ( toml_path : Option < & str > ) -> ( String , Vec < CrateSource > ) {
230
268
let toml_path = PathBuf :: from (
231
269
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>) {
234
272
let toml_filename = toml_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . to_string ( ) ;
235
273
let toml_content: String =
236
274
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 =
238
276
toml:: from_str ( & toml_content) . unwrap_or_else ( |e| panic ! ( "Failed to parse {}: \n {}" , toml_path. display( ) , e) ) ;
239
277
// parse the hashmap of the toml file into a list of crates
240
278
let tomlcrates: Vec < TomlCrate > = crate_list
@@ -288,7 +326,7 @@ fn read_crates(toml_path: Option<&str>) -> (String, Vec<CrateSource>) {
288
326
( toml_filename, crate_sources)
289
327
}
290
328
291
- // extract interesting data from a json lint message
329
+ /// Parse the json output of clippy and return a `ClippyWarning`
292
330
fn parse_json_message ( json_message : & str , krate : & Crate ) -> ClippyWarning {
293
331
let jmsg: Value = serde_json:: from_str ( & json_message) . unwrap_or_else ( |e| panic ! ( "Failed to parse json:\n {:?}" , e) ) ;
294
332
@@ -309,11 +347,31 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
309
347
. into ( ) ,
310
348
linttype : jmsg[ "message" ] [ "code" ] [ "code" ] . to_string ( ) . trim_matches ( '"' ) . into ( ) ,
311
349
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: " ) ,
313
351
}
314
352
}
315
353
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
317
375
pub fn run ( clap_config : & ArgMatches ) {
318
376
let cargo_clippy_path: PathBuf = PathBuf :: from ( "target/debug/cargo-clippy" ) ;
319
377
@@ -374,32 +432,16 @@ pub fn run(clap_config: &ArgMatches) {
374
432
. collect ( )
375
433
} ;
376
434
377
- // generate some stats:
435
+ // generate some stats
436
+ let stats_formatted = gather_stats ( & clippy_warnings) ;
378
437
379
438
// grab crashes/ICEs, save the crate name and the ice message
380
439
let ices: Vec < ( & String , & String ) > = clippy_warnings
381
440
. iter ( )
382
- . filter ( |warning| warning. ice )
441
+ . filter ( |warning| warning. is_ice )
383
442
. map ( |w| ( & w. crate_name , & w. message ) )
384
443
. collect ( ) ;
385
444
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
-
403
445
let mut all_msgs: Vec < String > = clippy_warnings. iter ( ) . map ( |warning| warning. to_string ( ) ) . collect ( ) ;
404
446
all_msgs. sort ( ) ;
405
447
all_msgs. push ( "\n \n \n \n Stats\n \n " . into ( ) ) ;
0 commit comments