Skip to content

Commit 8642fc9

Browse files
committed
multiple_crate_versions: skip dev and build deps
1 parent cff5cff commit 8642fc9

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

clippy_lints/src/multiple_crate_versions.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
//! lint on multiple versions of a crate being used
22
33
use crate::utils::{run_lints, span_lint};
4+
use rustc_hir::def_id::LOCAL_CRATE;
45
use rustc_hir::{Crate, CRATE_HIR_ID};
56
use rustc_lint::{LateContext, LateLintPass};
67
use rustc_session::{declare_lint_pass, declare_tool_lint};
78
use rustc_span::source_map::DUMMY_SP;
89

10+
use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId};
11+
use if_chain::if_chain;
912
use itertools::Itertools;
1013

1114
declare_clippy_lint! {
@@ -34,37 +37,65 @@ declare_clippy_lint! {
3437
declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]);
3538

3639
impl LateLintPass<'_, '_> for MultipleCrateVersions {
40+
#[allow(clippy::find_map)]
3741
fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
3842
if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
3943
return;
4044
}
4145

42-
let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() {
46+
let metadata = if let Ok(metadata) = MetadataCommand::new().exec() {
4347
metadata
4448
} else {
4549
span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
46-
4750
return;
4851
};
4952

53+
let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str();
5054
let mut packages = metadata.packages;
5155
packages.sort_by(|a, b| a.name.cmp(&b.name));
5256

53-
for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) {
54-
let group: Vec<cargo_metadata::Package> = group.collect();
57+
if_chain! {
58+
if let Some(resolve) = &metadata.resolve;
59+
if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id);
60+
then {
61+
for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
62+
let group: Vec<&Package> = group.collect();
63+
64+
if group.len() <= 1 {
65+
continue;
66+
}
5567

56-
if group.len() > 1 {
57-
let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect();
58-
versions.sort();
59-
let versions = versions.iter().join(", ");
68+
if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) {
69+
let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect();
70+
versions.sort();
71+
let versions = versions.iter().join(", ");
6072

61-
span_lint(
62-
cx,
63-
MULTIPLE_CRATE_VERSIONS,
64-
DUMMY_SP,
65-
&format!("multiple versions for dependency `{}`: {}", name, versions),
66-
);
73+
span_lint(
74+
cx,
75+
MULTIPLE_CRATE_VERSIONS,
76+
DUMMY_SP,
77+
&format!("multiple versions for dependency `{}`: {}", name, versions),
78+
);
79+
}
80+
}
6781
}
6882
}
6983
}
7084
}
85+
86+
fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
87+
fn depends_on(node: &Node, dep_id: &PackageId) -> bool {
88+
node.deps.iter().any(|dep| {
89+
dep.pkg == *dep_id
90+
&& dep
91+
.dep_kinds
92+
.iter()
93+
.any(|info| matches!(info.kind, DependencyKind::Normal))
94+
})
95+
}
96+
97+
nodes
98+
.iter()
99+
.filter(|node| depends_on(node, dep_id))
100+
.any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id))
101+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Should not lint for dev or build dependencies. See issue 5041.
2+
3+
[package]
4+
name = "multiple_crate_versions"
5+
version = "0.1.0"
6+
publish = false
7+
8+
# One of the versions of winapi is only a dev dependency: allowed
9+
[dependencies]
10+
ctrlc = "=3.1.0"
11+
[dev-dependencies]
12+
ansi_term = "=0.11.0"
13+
14+
# Both versions of winapi are a build dependency: allowed
15+
[build-dependencies]
16+
ctrlc = "=3.1.0"
17+
ansi_term = "=0.11.0"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// compile-flags: --crate-name=multiple_crate_versions
2+
#![warn(clippy::multiple_crate_versions)]
3+
4+
fn main() {}

0 commit comments

Comments
 (0)