Skip to content

Commit

Permalink
lintcheck: move out of clippy-dev into own crate
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaskrgr committed Mar 11, 2021
1 parent 99afc6e commit 2546e6f
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .cargo/config
@@ -1,7 +1,7 @@
[alias]
uitest = "test --test compile-test"
dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
dev-lintcheck = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck"
dev-lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "

[build]
rustflags = ["-Zunstable-options"]
9 changes: 0 additions & 9 deletions clippy_dev/Cargo.toml
Expand Up @@ -7,20 +7,11 @@ edition = "2018"
[dependencies]
bytecount = "0.6"
clap = "2.33"
flate2 = { version = "1.0.19", optional = true }
fs_extra = { version = "1.2.0", optional = true }
itertools = "0.9"
opener = "0.4"
regex = "1"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
shell-escape = "0.1"
tar = { version = "0.4.30", optional = true }
toml = { version = "0.5", optional = true }
ureq = { version = "2.0.0-rc3", optional = true }
rayon = { version = "1.5.0", optional = true }
walkdir = "2"

[features]
lintcheck = ["flate2", "serde_json", "tar", "toml", "ureq", "serde", "fs_extra", "rayon"]
deny-warnings = []
1 change: 0 additions & 1 deletion clippy_dev/src/lib.rs
Expand Up @@ -12,7 +12,6 @@ use walkdir::WalkDir;

pub mod bless;
pub mod fmt;
pub mod lintcheck;
pub mod new_lint;
pub mod ra_setup;
pub mod serve;
Expand Down
45 changes: 3 additions & 42 deletions clippy_dev/src/main.rs
Expand Up @@ -2,21 +2,13 @@

use clap::{App, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};

#[cfg(feature = "lintcheck")]
use clippy_dev::lintcheck;

fn main() {
let matches = get_clap_config();

match matches.subcommand() {
("bless", Some(matches)) => {
bless::bless(matches.is_present("ignore-timestamp"));
},
#[cfg(feature = "lintcheck")]
("lintcheck", Some(matches)) => {
lintcheck::run(&matches);
},
("fmt", Some(matches)) => {
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
},
Expand Down Expand Up @@ -53,34 +45,7 @@ fn main() {
}

fn get_clap_config<'a>() -> ArgMatches<'a> {
#[cfg(feature = "lintcheck")]
let lintcheck_sbcmd = SubCommand::with_name("lintcheck")
.about("run clippy on a set of crates and check output")
.arg(
Arg::with_name("only")
.takes_value(true)
.value_name("CRATE")
.long("only")
.help("only process a single crate of the list"),
)
.arg(
Arg::with_name("crates-toml")
.takes_value(true)
.value_name("CRATES-SOURCES-TOML-PATH")
.long("crates-toml")
.help("set the path for a crates.toml where lintcheck should read the sources from"),
)
.arg(
Arg::with_name("threads")
.takes_value(true)
.value_name("N")
.short("j")
.long("jobs")
.help("number of threads to use, 0 automatic choice"),
)
.arg(Arg::with_name("fix").help("runs cargo clippy --fix and checks if all suggestions apply"));

let app = App::new("Clippy developer tooling")
App::new("Clippy developer tooling")
.subcommand(
SubCommand::with_name("bless")
.about("bless the test output changes")
Expand Down Expand Up @@ -197,10 +162,6 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.validator_os(serve::validate_port),
)
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
);

#[cfg(feature = "lintcheck")]
let app = app.subcommand(lintcheck_sbcmd);

app.get_matches()
)
.get_matches()
}
19 changes: 19 additions & 0 deletions lintcheck/Cargo.toml
@@ -0,0 +1,19 @@
[package]
name = "lintcheck"
version = "0.0.1"
authors = ["Matthias Krüger <matthias.krueger@famsik.de>"]
edition = "2018"

[dependencies]
clap = "2.33"
flate2 = {version = "1.0.19"}
fs_extra = {version = "1.2.0"}
rayon = {version = "1.5.0"}
serde = {version = "1.0", features = ["derive"]}
serde_json = {version = "1.0"}
tar = {version = "0.4.30"}
toml = {version = "0.5"}
ureq = {version = "2.0.0-rc3"}

[features]
deny-warnings = []
77 changes: 77 additions & 0 deletions lintcheck/README.md
@@ -0,0 +1,77 @@
# Clippy Dev Tool

The Clippy Dev Tool is a tool to ease Clippy development, similar to `rustc`s
`x.py`.

Functionalities (incomplete):

## `lintcheck`

Runs clippy on a fixed set of crates read from
`clippy_dev/lintcheck_crates.toml` and saves logs of the lint warnings into the
repo. We can then check the diff and spot new or disappearing warnings.

From the repo root, run:

```
cargo run --target-dir clippy_dev/target --package clippy_dev \
--bin clippy_dev --manifest-path clippy_dev/Cargo.toml --features lintcheck -- lintcheck
```

or

```
cargo dev-lintcheck
```

By default the logs will be saved into
`lintcheck-logs/lintcheck_crates_logs.txt`.

You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
`LINTCHECK_TOML="custom.toml"` where `custom.toml` must be a relative path from
the repo root.

The results will then be saved to `lintcheck-logs/custom_logs.toml`.

### Configuring the Crate Sources

The sources to check are saved in a `toml` file. There are three types of
sources.

1. Crates-io Source

```toml
bitflags = {name = "bitflags", versions = ['1.2.1']}
```
Requires a "name" and one or multiple "versions" to be checked.

2. `git` Source
````toml
puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
````
Requires a name, the url to the repo and unique identifier of a commit,
branch or tag which is checked out before linting. There is no way to always
check `HEAD` because that would lead to changing lint-results as the repo
would get updated. If `git_url` or `git_hash` is missing, an error will be
thrown.

3. Local Dependency
```toml
clippy = {name = "clippy", path = "/home/user/clippy"}
```
For when you want to add a repository that is not published yet.

#### Command Line Options (optional)

```toml
bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
```

It is possible to specify command line options for each crate. This makes it
possible to only check a crate for certain lint groups. If no options are
specified, the lint groups `clippy::all`, `clippy::pedantic`, and
`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
is checked.

**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
is explicitly specified in the options.
35 changes: 35 additions & 0 deletions lintcheck/lintcheck_crates.toml
@@ -0,0 +1,35 @@
[crates]
# some of these are from cargotest
cargo = {name = "cargo", versions = ['0.49.0']}
iron = {name = "iron", versions = ['0.6.1']}
ripgrep = {name = "ripgrep", versions = ['12.1.1']}
xsv = {name = "xsv", versions = ['0.13.0']}
# commented out because of 173K clippy::match_same_arms msgs in language_type.rs
#tokei = { name = "tokei", versions = ['12.0.4']}
rayon = {name = "rayon", versions = ['1.5.0']}
serde = {name = "serde", versions = ['1.0.118']}
# top 10 crates.io dls
bitflags = {name = "bitflags", versions = ['1.2.1']}
# crash = {name = "clippy_crash", path = "/tmp/clippy_crash"}
libc = {name = "libc", versions = ['0.2.81']}
log = {name = "log", versions = ['0.4.11']}
proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']}
quote = {name = "quote", versions = ['1.0.7']}
rand = {name = "rand", versions = ['0.7.3']}
rand_core = {name = "rand_core", versions = ['0.6.0']}
regex = {name = "regex", versions = ['1.3.2']}
syn = {name = "syn", versions = ['1.0.54']}
unicode-xid = {name = "unicode-xid", versions = ['0.2.1']}
# some more of dtolnays crates
anyhow = {name = "anyhow", versions = ['1.0.38']}
async-trait = {name = "async-trait", versions = ['0.1.42']}
cxx = {name = "cxx", versions = ['1.0.32']}
ryu = {name = "ryu", version = ['1.0.5']}
serde_yaml = {name = "serde_yaml", versions = ['0.8.17']}
thiserror = {name = "thiserror", versions = ['1.0.24']}
# some embark crates, there are other interesting crates but
# unfortunately adding them increases lintcheck runtime drastically
cfg-expr = {name = "cfg-expr", versions = ['0.7.1']}
puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
rpmalloc = {name = "rpmalloc", versions = ['0.2.0']}
tame-oidc = {name = "tame-oidc", versions = ['0.1.0']}
70 changes: 64 additions & 6 deletions clippy_dev/src/lintcheck.rs → lintcheck/src/main.rs
@@ -1,14 +1,11 @@
// Run clippy on a fixed set of crates and collect the warnings.
// This helps observing the impact clippy changs have on a set of real-world code.
// This helps observing the impact clippy changes have on a set of real-world code (and not just our testsuite).
//
// When a new lint is introduced, we can search the results for new warnings and check for false
// positives.

#![cfg(feature = "lintcheck")]
#![allow(clippy::filter_map, clippy::collapsible_else_if)]

use crate::clippy_project_root;

use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{collections::HashMap, io::ErrorKind};
Expand All @@ -18,7 +15,7 @@ use std::{
path::{Path, PathBuf},
};

use clap::ArgMatches;
use clap::{App, Arg, ArgMatches, SubCommand};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand Down Expand Up @@ -564,7 +561,9 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
/// # Panics
///
/// This function panics if the clippy binaries don't exist.
pub fn run(clap_config: &ArgMatches) {
pub fn main() {
let clap_config = &get_clap_config();

let config = LintcheckConfig::from_clap(clap_config);

println!("Compiling clippy...");
Expand Down Expand Up @@ -800,6 +799,65 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
});
}

fn get_clap_config<'a>() -> ArgMatches<'a> {
let lintcheck_sbcmd = SubCommand::with_name("lintcheck")
.about("run clippy on a set of crates and check output")
.arg(
Arg::with_name("only")
.takes_value(true)
.value_name("CRATE")
.long("only")
.help("only process a single crate of the list"),
)
.arg(
Arg::with_name("crates-toml")
.takes_value(true)
.value_name("CRATES-SOURCES-TOML-PATH")
.long("crates-toml")
.help("set the path for a crates.toml where lintcheck should read the sources from"),
)
.arg(
Arg::with_name("threads")
.takes_value(true)
.value_name("N")
.short("j")
.long("jobs")
.help("number of threads to use, 0 automatic choice"),
)
.arg(Arg::with_name("fix").help("runs cargo clippy --fix and checks if all suggestions apply"));

let app = App::new("Clippy developer tooling");

let app = app.subcommand(lintcheck_sbcmd);

app.get_matches()
}

/// Returns the path to the Clippy project directory
///
/// # Panics
///
/// Panics if the current directory could not be retrieved, there was an error reading any of the
/// Cargo.toml files or ancestor directory is the clippy root directory
#[must_use]
pub fn clippy_project_root() -> PathBuf {
let current_dir = std::env::current_dir().unwrap();
for path in current_dir.ancestors() {
let result = std::fs::read_to_string(path.join("Cargo.toml"));
if let Err(err) = &result {
if err.kind() == std::io::ErrorKind::NotFound {
continue;
}
}

let content = result.unwrap();
if content.contains("[package]\nname = \"clippy\"") {
return path.to_path_buf();
}
}
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
}

#[test]
fn lintcheck_test() {
let args = [
Expand Down

0 comments on commit 2546e6f

Please sign in to comment.