1 //! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
2 //! long error messages
4 //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
6 // Dogfood cannot run on Windows
9 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
10 #![warn(rust_2018_idioms, unused_lifetimes)]
12 use std::lazy::SyncLazy;
13 use std::path::PathBuf;
14 use std::process::Command;
18 static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
19 let mut path = std::env::current_exe().unwrap();
20 assert!(path.pop()); // deps
21 path.set_file_name("cargo-clippy");
27 // run clippy on itself and fail the test if lint warnings are reported
28 if cargo::is_rustc_test_suite() {
31 let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
33 let mut command = Command::new(&*CLIPPY_PATH);
35 .current_dir(root_dir)
36 .env("CARGO_INCREMENTAL", "0")
39 .arg("--all-features")
41 .args(&["-D", "clippy::all"])
42 .args(&["-D", "clippy::pedantic"])
43 .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
45 // internal lints only exist if we build with the internal feature
46 if cfg!(feature = "internal") {
47 command.args(&["-D", "clippy::internal"]);
50 let output = command.output().unwrap();
52 println!("status: {}", output.status);
53 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
54 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
56 assert!(output.status.success());
59 fn test_no_deps_ignores_path_deps_in_workspaces() {
60 if cargo::is_rustc_test_suite() {
63 let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
64 let target_dir = root.join("target").join("dogfood");
65 let cwd = root.join("clippy_workspace_tests");
67 // Make sure we start with a clean state
70 .env("CARGO_TARGET_DIR", &target_dir)
72 .args(&["-p", "subcrate"])
73 .args(&["-p", "path_dep"])
77 // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
78 // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
79 let output = Command::new(&*CLIPPY_PATH)
81 .env("CARGO_INCREMENTAL", "0")
83 .args(&["-p", "subcrate"])
86 .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
87 .args(&["--cfg", r#"feature="primary_package_test""#])
90 println!("status: {}", output.status);
91 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
92 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
94 assert!(output.status.success());
96 let lint_path_dep = || {
97 // Test that without the `--no-deps` argument, `path_dep` is linted.
98 let output = Command::new(&*CLIPPY_PATH)
100 .env("CARGO_INCREMENTAL", "0")
102 .args(&["-p", "subcrate"])
104 .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
105 .args(&["--cfg", r#"feature="primary_package_test""#])
108 println!("status: {}", output.status);
109 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
110 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
112 assert!(!output.status.success());
114 String::from_utf8(output.stderr)
116 .contains("error: empty `loop {}` wastes CPU cycles")
120 // Make sure Cargo is aware of the removal of `--no-deps`.
123 let successful_build = || {
124 let output = Command::new(&*CLIPPY_PATH)
126 .env("CARGO_INCREMENTAL", "0")
128 .args(&["-p", "subcrate"])
130 .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
133 println!("status: {}", output.status);
134 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
135 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
137 assert!(output.status.success());
142 // Trigger a sucessful build, so Cargo would like to cache the build result.
145 // Make sure there's no spurious rebuild when nothing changes.
146 let stderr = String::from_utf8(successful_build().stderr).unwrap();
147 assert!(!stderr.contains("Compiling"));
148 assert!(!stderr.contains("Checking"));
149 assert!(stderr.contains("Finished"));
151 // Make sure Cargo is aware of the new `--cfg` flag.
156 fn dogfood_subprojects() {
157 // run clippy on remaining subprojects and fail the test if lint warnings are reported
158 if cargo::is_rustc_test_suite() {
162 // NOTE: `path_dep` crate is omitted on purpose here
164 "clippy_workspace_tests",
165 "clippy_workspace_tests/src",
166 "clippy_workspace_tests/subcrate",
167 "clippy_workspace_tests/subcrate/src",
173 run_clippy_for_project(project);
176 // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
177 // same time, so we test this immediately after the dogfood for workspaces.
178 test_no_deps_ignores_path_deps_in_workspaces();
183 #[cfg(feature = "internal")]
184 fn run_metadata_collection_lint() {
186 use std::time::SystemTime;
188 // Setup for validation
189 let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
190 let start_time = SystemTime::now();
192 // Run collection as is
193 std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
194 run_clippy_for_project("clippy_lints");
196 // Check if cargo caching got in the way
197 if let Ok(file) = File::open(metadata_output_path) {
198 if let Ok(metadata) = file.metadata() {
199 if let Ok(last_modification) = metadata.modified() {
200 if last_modification > start_time {
201 // The output file has been modified. Most likely by a hungry
202 // metadata collection monster. So We'll return.
209 // Force cargo to invalidate the caches
210 filetime::set_file_mtime(
211 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
212 filetime::FileTime::now(),
216 // Running the collection again
217 run_clippy_for_project("clippy_lints");
220 fn run_clippy_for_project(project: &str) {
221 let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
223 let mut command = Command::new(&*CLIPPY_PATH);
226 .current_dir(root_dir.join(project))
227 .env("CARGO_INCREMENTAL", "0")
229 .arg("--all-targets")
230 .arg("--all-features")
232 .args(&["-D", "clippy::all"])
233 .args(&["-D", "clippy::pedantic"])
234 .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
236 // internal lints only exist if we build with the internal feature
237 if cfg!(feature = "internal") {
238 command.args(&["-D", "clippy::internal"]);
241 let output = command.output().unwrap();
243 println!("status: {}", output.status);
244 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
245 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
247 assert!(output.status.success());