1 //! lint on multiple versions of a crate being used
3 use crate::utils
::{run_lints, span_lint}
;
4 use rustc_hir
::def_id
::LOCAL_CRATE
;
5 use rustc_hir
::{Crate, CRATE_HIR_ID}
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
8 use rustc_span
::source_map
::DUMMY_SP
;
10 use cargo_metadata
::{DependencyKind, Node, Package, PackageId}
;
11 use if_chain
::if_chain
;
12 use itertools
::Itertools
;
14 declare_clippy_lint
! {
15 /// **What it does:** Checks to see if multiple versions of a crate are being
18 /// **Why is this bad?** This bloats the size of targets, and can lead to
19 /// confusing error messages when structs or traits are used interchangeably
20 /// between different versions of a crate.
22 /// **Known problems:** Because this can be caused purely by the dependencies
23 /// themselves, it's not always possible to fix this issue.
27 /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
30 /// ansi_term = "=0.11.0"
32 pub MULTIPLE_CRATE_VERSIONS
,
34 "multiple versions of the same crate being used"
37 declare_lint_pass
!(MultipleCrateVersions
=> [MULTIPLE_CRATE_VERSIONS
]);
39 impl LateLintPass
<'_
> for MultipleCrateVersions
{
40 fn check_crate(&mut self, cx
: &LateContext
<'_
>, _
: &Crate
<'_
>) {
41 if !run_lints(cx
, &[MULTIPLE_CRATE_VERSIONS
], CRATE_HIR_ID
) {
45 let metadata
= unwrap_cargo_metadata
!(cx
, MULTIPLE_CRATE_VERSIONS
, true);
46 let local_name
= cx
.tcx
.crate_name(LOCAL_CRATE
).as_str();
47 let mut packages
= metadata
.packages
;
48 packages
.sort_by(|a
, b
| a
.name
.cmp(&b
.name
));
51 if let Some(resolve
) = &metadata
.resolve
;
52 if let Some(local_id
) = packages
54 .find_map(|p
| if p
.name
== *local_name { Some(&p.id) }
else { None }
);
56 for (name
, group
) in &packages
.iter().group_by(|p
| p
.name
.clone()) {
57 let group
: Vec
<&Package
> = group
.collect();
63 if group
.iter().all(|p
| is_normal_dep(&resolve
.nodes
, local_id
, &p
.id
)) {
64 let mut versions
: Vec
<_
> = group
.into_iter().map(|p
| &p
.version
).collect();
66 let versions
= versions
.iter().join(", ");
70 MULTIPLE_CRATE_VERSIONS
,
72 &format
!("multiple versions for dependency `{}`: {}", name
, versions
),
81 fn is_normal_dep(nodes
: &[Node
], local_id
: &PackageId
, dep_id
: &PackageId
) -> bool
{
82 fn depends_on(node
: &Node
, dep_id
: &PackageId
) -> bool
{
83 node
.deps
.iter().any(|dep
| {
88 .any(|info
| matches
!(info
.kind
, DependencyKind
::Normal
))
94 .filter(|node
| depends_on(node
, dep_id
))
95 .any(|node
| node
.id
== *local_id
|| is_normal_dep(nodes
, local_id
, &node
.id
))