-// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
//! lint on multiple versions of a crate being used
-use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
-use crate::rustc::{declare_tool_lint, lint_array};
-use crate::syntax::{ast::*, source_map::DUMMY_SP};
-use crate::utils::span_lint;
+use crate::utils::{run_lints, span_lint};
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_hir::{Crate, CRATE_HIR_ID};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::DUMMY_SP;
-use cargo_metadata;
+use cargo_metadata::{DependencyKind, Node, Package, PackageId};
+use if_chain::if_chain;
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! {
+ /// **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.x and v0.2.x, triggering a warning.
+ /// [dependencies]
+ /// ctrlc = "=3.1.0"
+ /// ansi_term = "=0.11.0"
+ /// ```
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<'_>, _: &Crate) {
- let metadata = if let Ok(metadata) = cargo_metadata::metadata_deps(None, true) {
- metadata
- } else {
- span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
+declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]);
+impl LateLintPass<'_> for MultipleCrateVersions {
+ fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+ if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
return;
- };
+ }
+ let metadata = unwrap_cargo_metadata!(cx, MULTIPLE_CRATE_VERSIONS, true);
+ let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str();
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<cargo_metadata::Package> = group.collect();
+ if_chain! {
+ if let Some(resolve) = &metadata.resolve;
+ if let Some(local_id) = packages
+ .iter()
+ .find_map(|p| if p.name == *local_name { Some(&p.id) } else { None });
+ then {
+ for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
+ let group: Vec<&Package> = group.collect();
- if group.len() > 1 {
- let versions = group.into_iter().map(|p| p.version).join(", ");
+ if group.len() <= 1 {
+ continue;
+ }
- span_lint(
- cx,
- MULTIPLE_CRATE_VERSIONS,
- DUMMY_SP,
- &format!("multiple versions for dependency `{}`: {}", name, versions),
- );
+ if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) {
+ let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect();
+ versions.sort();
+ let versions = versions.iter().join(", ");
+
+ span_lint(
+ cx,
+ MULTIPLE_CRATE_VERSIONS,
+ DUMMY_SP,
+ &format!("multiple versions for dependency `{}`: {}", name, versions),
+ );
+ }
+ }
}
}
}
}
+
+fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
+ fn depends_on(node: &Node, dep_id: &PackageId) -> bool {
+ node.deps.iter().any(|dep| {
+ dep.pkg == *dep_id
+ && dep
+ .dep_kinds
+ .iter()
+ .any(|info| matches!(info.kind, DependencyKind::Normal))
+ })
+ }
+
+ nodes
+ .iter()
+ .filter(|node| depends_on(node, dep_id))
+ .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id))
+}