Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let Cargo track CLIPPY_ARGS #6834

Merged
merged 4 commits into from Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ the lint(s) you are interested in:
```terminal
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
```
Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`.

### Specifying the minimum supported Rust version

Expand Down
54 changes: 46 additions & 8 deletions src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_span;

use rustc_interface::interface;
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
Expand Down Expand Up @@ -59,20 +63,53 @@ fn test_arg_value() {
assert_eq!(arg_value(args, "--foo", |_| true), None);
}

fn track_clippy_args(sess: &Session, args_env_var: &Option<String>) {
sess.parse_sess.env_depinfo.borrow_mut().insert((
Symbol::intern("CLIPPY_ARGS"),
args_env_var.as_deref().map(Symbol::intern),
));
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of
/// `CLIPPY_ARGS` environment variable.
struct RustcCallbacks {
clippy_args_var: Option<String>,
}

impl rustc_driver::Callbacks for RustcCallbacks {
fn config(&mut self, config: &mut interface::Config) {
let previous = config.register_lints.take();
let clippy_args_var = self.clippy_args_var.take();
config.register_lints = Some(Box::new(move |sess, lint_store| {
flip1995 marked this conversation as resolved.
Show resolved Hide resolved
if let Some(ref previous) = previous {
(previous)(sess, lint_store);
}

track_clippy_args(sess, &clippy_args_var);
}));
}
}

struct ClippyCallbacks {
clippy_args_var: Option<String>,
}

impl rustc_driver::Callbacks for ClippyCallbacks {
fn config(&mut self, config: &mut interface::Config) {
let previous = config.register_lints.take();
let clippy_args_var = self.clippy_args_var.take();
config.register_lints = Some(Box::new(move |sess, mut lint_store| {
// technically we're ~guaranteed that this is none but might as well call anything that
// is there already. Certainly it can't hurt.
if let Some(previous) = &previous {
(previous)(sess, lint_store);
}

track_clippy_args(sess, &clippy_args_var);

let conf = clippy_lints::read_conf(&[], &sess);
clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
clippy_lints::register_pre_expansion_lints(&mut lint_store);
Expand Down Expand Up @@ -277,7 +314,9 @@ pub fn main() {
};

let mut no_deps = false;
let clippy_args = env::var("CLIPPY_ARGS")
let clippy_args_var = env::var("CLIPPY_ARGS").ok();
let clippy_args = clippy_args_var
.as_deref()
.unwrap_or_default()
.split("__CLIPPY_HACKERY__")
.filter_map(|s| match s {
Expand Down Expand Up @@ -305,11 +344,10 @@ pub fn main() {
args.extend(clippy_args);
}

let mut clippy = ClippyCallbacks;
let mut default = DefaultCallbacks;
let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
if clippy_enabled { &mut clippy } else { &mut default };

rustc_driver::RunCompiler::new(&args, callbacks).run()
if clippy_enabled {
rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
} else {
rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
}
}))
}
105 changes: 70 additions & 35 deletions tests/dogfood.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![feature(once_cell)]

use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process::Command;

mod cargo;
Expand Down Expand Up @@ -46,40 +46,53 @@ fn dogfood_clippy() {
assert!(output.status.success());
}

#[test]
fn dogfood_subprojects() {
fn test_no_deps_ignores_path_deps_in_workspaces() {
fn clean(cwd: &Path, target_dir: &Path) {
Command::new("cargo")
.current_dir(cwd)
.env("CARGO_TARGET_DIR", target_dir)
.arg("clean")
.args(&["-p", "subcrate"])
.args(&["-p", "path_dep"])
.output()
.unwrap();
}

if cargo::is_rustc_test_suite() {
return;
}
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let target_dir = root.join("target").join("dogfood");
let cwd = root.join("clippy_workspace_tests");

// Make sure we start with a clean state
clean(&cwd, &target_dir);

// `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
// Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
fn test_no_deps_ignores_path_deps_in_workspaces() {
if cargo::is_rustc_test_suite() {
return;
}
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let target_dir = root.join("target").join("dogfood");
let cwd = root.join("clippy_workspace_tests");

// Make sure we start with a clean state
Command::new("cargo")
.current_dir(&cwd)
.env("CARGO_TARGET_DIR", &target_dir)
.arg("clean")
.args(&["-p", "subcrate"])
.args(&["-p", "path_dep"])
.output()
.unwrap();

// `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
// Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
.arg("--")
.arg("--no-deps")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
.args(&["--cfg", r#"feature="primary_package_test""#])
.output()
.unwrap();
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

assert!(output.status.success());

let lint_path_dep = || {
// Test that without the `--no-deps` argument, `path_dep` is linted.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
.arg("--")
.arg("--no-deps")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
.args(&["--cfg", r#"feature="primary_package_test""#])
.output()
Expand All @@ -88,12 +101,18 @@ fn dogfood_subprojects() {
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

assert!(output.status.success());
assert!(!output.status.success());
assert!(
String::from_utf8(output.stderr)
.unwrap()
.contains("error: empty `loop {}` wastes CPU cycles")
);
};

// Make sure we start with a clean state
clean(&cwd, &target_dir);
// Make sure Cargo is aware of the removal of `--no-deps`.
lint_path_dep();

// Test that without the `--no-deps` argument, `path_dep` is linted.
let successful_build = || {
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
Expand All @@ -102,16 +121,32 @@ fn dogfood_subprojects() {
.args(&["-p", "subcrate"])
.arg("--")
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
.args(&["--cfg", r#"feature="primary_package_test""#])
.output()
.unwrap();
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

assert!(!output.status.success());
}
assert!(output.status.success());

output
};

// Trigger a sucessful build, so Cargo would like to cache the build result.
successful_build();

// Make sure there's no spurious rebuild when nothing changes.
let stderr = String::from_utf8(successful_build().stderr).unwrap();
assert!(!stderr.contains("Compiling"));
assert!(!stderr.contains("Checking"));
assert!(stderr.contains("Finished"));

// Make sure Cargo is aware of the new `--cfg` flag.
lint_path_dep();
}

#[test]
fn dogfood_subprojects() {
// run clippy on remaining subprojects and fail the test if lint warnings are reported
if cargo::is_rustc_test_suite() {
return;
Expand Down