From 7de706b34b4ff04f58a2a5c3f72a7b0cab3488f6 Mon Sep 17 00:00:00 2001 From: Yusuf Simonson Date: Mon, 30 Apr 2018 06:20:39 +0700 Subject: [PATCH] Lint for multiple versions of dependencies --- README.md | 1 + clippy_lints/Cargo.toml | 1 + clippy_lints/src/lib.rs | 10 +++ clippy_lints/src/multiple_crate_versions.rs | 72 +++++++++++++++++++++ util/lintlib.py | 1 + util/update_lints.py | 1 + 6 files changed, 86 insertions(+) create mode 100644 clippy_lints/src/multiple_crate_versions.rs diff --git a/README.md b/README.md index 208aa550882..50c2bd95b7f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ We have a bunch of lint categories to allow you to choose how much clippy is sup * `clippy_style` (code that should be written in a more idiomatic way) * `clippy_complexity` (code that does something simple but in a complex way) * `clippy_perf` (code that can be written in a faster way) +* `clippy_cargo` (checks against the cargo manifest) * **`clippy_correctness`** (code that is just outright wrong or very very useless) More to come, please [file an issue](https://github.com/rust-lang-nursery/rust-clippy/issues) if you have ideas! diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 17c1c25669f..e17aa75cdf5 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -16,6 +16,7 @@ license = "MPL-2.0" keywords = ["clippy", "lint", "plugin"] [dependencies] +cargo_metadata = "0.5" itertools = "0.7" lazy_static = "1.0" matches = "0.1.2" diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a952f5081f4..0a500f9ad6e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -16,6 +16,7 @@ #![feature(iterator_find_map)] +extern crate cargo_metadata; #[macro_use] extern crate rustc; extern crate rustc_typeck; @@ -81,6 +82,9 @@ macro_rules! declare_clippy_lint { { pub $name:tt, restriction, $description:tt } => { declare_lint! { pub $name, Allow, $description } }; + { pub $name:tt, cargo, $description:tt } => { + declare_lint! { pub $name, Allow, $description } + }; { pub $name:tt, nursery, $description:tt } => { declare_lint! { pub $name, Allow, $description } }; @@ -158,6 +162,7 @@ pub mod minmax; pub mod misc; pub mod misc_early; pub mod missing_doc; +pub mod multiple_crate_versions; pub mod mut_mut; pub mod mut_reference; pub mod mutex_atomic; @@ -412,6 +417,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { reg.register_late_lint_pass(box question_mark::QuestionMarkPass); reg.register_late_lint_pass(box suspicious_trait_impl::SuspiciousImpl); reg.register_late_lint_pass(box redundant_field_names::RedundantFieldNames); + reg.register_early_lint_pass(box multiple_crate_versions::Pass); reg.register_late_lint_pass(box map_unit_fn::Pass); reg.register_late_lint_pass(box infallible_destructuring_match::Pass); @@ -895,6 +901,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { vec::USELESS_VEC, ]); + reg.register_lint_group("clippy_cargo", vec![ + multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, + ]); + reg.register_lint_group("clippy_nursery", vec![ attrs::EMPTY_LINE_AFTER_OUTER_ATTR, fallible_impl_from::FALLIBLE_IMPL_FROM, diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs new file mode 100644 index 00000000000..d347270236d --- /dev/null +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -0,0 +1,72 @@ +//! lint on multiple versions of a crate being used + +use rustc::lint::*; +use syntax::ast::*; + +use cargo_metadata; +use itertools::Itertools; + +/// **What it does:** Checks to see if multiple versions of a crate are being +/// used. +/// +/// **Why is this bad?** This bloats the size of targets, and can lead to +/// confusing error messages when structs or traits are used interchangeably +/// between different versions of a crate. +/// +/// **Known problems:** Because this can be caused purely by the dependencies +/// themselves, it's not always possible to fix this issue. +/// +/// **Example:** +/// ```toml +/// # This will pull in both winapi v0.3.4 and v0.2.8, triggering a warning. +/// [dependencies] +/// ctrlc = "3.1.0" +/// ansi_term = "0.11.0" +/// ``` +declare_clippy_lint! { + pub MULTIPLE_CRATE_VERSIONS, + cargo, + "multiple versions of the same crate being used" +} + +pub struct Pass; + +impl LintPass for Pass { + fn get_lints(&self) -> LintArray { + lint_array!(MULTIPLE_CRATE_VERSIONS) + } +} + +impl EarlyLintPass for Pass { + fn check_crate(&mut self, cx: &EarlyContext, krate: &Crate) { + let metadata = match cargo_metadata::metadata_deps(None, true) { + Ok(metadata) => metadata, + Err(_) => { + cx.span_lint( + MULTIPLE_CRATE_VERSIONS, + krate.span, + "could not read cargo metadata" + ); + + return; + } + }; + + let mut packages = metadata.packages; + packages.sort_by(|a, b| a.name.cmp(&b.name)); + + for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) { + let group: Vec = group.collect(); + + if group.len() > 1 { + let versions = group.into_iter().map(|p| p.version).join(", "); + + cx.span_lint( + MULTIPLE_CRATE_VERSIONS, + krate.span, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } + } +} diff --git a/util/lintlib.py b/util/lintlib.py index c28177e1062..4323ef5c3e7 100644 --- a/util/lintlib.py +++ b/util/lintlib.py @@ -24,6 +24,7 @@ lint_levels = { "restriction": 'Allow', "pedantic": 'Allow', "nursery": 'Allow', + "cargo": 'Allow', } diff --git a/util/update_lints.py b/util/update_lints.py index 58caa5dac0d..692599886a9 100755 --- a/util/update_lints.py +++ b/util/update_lints.py @@ -129,6 +129,7 @@ def main(print_only=False, check=False): "perf": [], "restriction": [], "pedantic": [], + "cargo": [], "nursery": [], }