cargo/compiler/
unused_deps.rs1use std::collections::BTreeSet;
2
3use crate::util::data_structures::IndexMap;
4use crate::util::data_structures::IndexSet;
5use tracing::{instrument, trace};
6
7use super::BuildContext;
8use super::unit::Unit;
9use crate::compiler::build_config::CompileMode;
10use crate::util::interning::InternedString;
11use crate::workspace::Dependency;
12use crate::workspace::PackageId;
13use crate::workspace::dependency::DepKind;
14use crate::workspace::manifest::TargetKind;
15
16pub struct UnusedDepState {
18 pub states: IndexMap<PackageId, IndexMap<DepKind, DependenciesState>>,
19}
20
21impl UnusedDepState {
22 #[instrument(name = "UnusedDepState::new", skip_all)]
23 pub fn new(bcx: &BuildContext<'_, '_>) -> Self {
24 let mut root_build_script_builds = IndexSet::default();
26 let roots = &bcx.roots;
27 for root in roots.iter() {
28 for build_script_run in bcx.unit_graph[root].iter() {
29 if !build_script_run.unit.target.is_custom_build()
30 && build_script_run.unit.pkg.package_id() != root.pkg.package_id()
31 {
32 continue;
33 }
34 for build_script_build in bcx.unit_graph[&build_script_run.unit].iter() {
35 if !build_script_build.unit.target.is_custom_build()
36 && build_script_build.unit.pkg.package_id() != root.pkg.package_id()
37 {
38 continue;
39 }
40 if build_script_build.unit.mode != CompileMode::Build {
41 continue;
42 }
43 root_build_script_builds.insert(build_script_build.unit.clone());
44 }
45 }
46 }
47
48 trace!("selected dep kinds: {:?}", bcx.selected_dep_kinds);
49 let mut states = IndexMap::<_, IndexMap<_, DependenciesState>>::default();
50 for root in roots.iter().chain(root_build_script_builds.iter()) {
51 let pkg_id = root.pkg.package_id();
52 let dep_kind = dep_kind_of(root);
53 if !bcx.selected_dep_kinds.contains(dep_kind) {
54 trace!(
55 "pkg {} v{} ({dep_kind:?}): ignoring unused deps due to non-exhaustive units",
56 pkg_id.name(),
57 pkg_id.version(),
58 );
59 continue;
60 }
61 let sibling_targets = root.pkg.targets();
62 let siibling_lib_exists = || sibling_targets.iter().any(|target| target.is_lib());
63 let linkable_sibling_exists =
64 || sibling_targets.iter().any(|target| target.is_linkable());
65 let sibling_lib_present = || {
66 bcx.unit_graph.keys().any(|unit| {
67 unit.pkg.package_id() == pkg_id
68 && unit.target.is_lib()
69 && matches!(
70 unit.mode,
71 CompileMode::Build | CompileMode::Check { test: false }
72 )
73 })
74 };
75 if dep_kind == DepKind::Normal
76 && !root.target.is_lib()
77 && siibling_lib_exists()
78 && !linkable_sibling_exists()
80 && !sibling_lib_present()
81 {
82 trace!(
88 "pkg {} v{} ({dep_kind:?}): ignoring unused deps due to the lib's build being skipped",
89 pkg_id.name(),
90 pkg_id.version(),
91 );
92 continue;
93 }
94
95 trace!(
96 "tracking root {} {} ({:?})",
97 root.pkg.name(),
98 unit_desc(root),
99 dep_kind
100 );
101
102 let state = states
103 .entry(pkg_id)
104 .or_default()
105 .entry(dep_kind)
106 .or_default();
107 state.needed_units += 1;
108 for dep in bcx.unit_graph[root].iter() {
109 trace!(
110 " => {} (deps={})",
111 dep.unit.pkg.name(),
112 dep.manifest_deps.0.is_some()
113 );
114 let manifest_deps = if let Some(manifest_deps) = &dep.manifest_deps.0 {
115 Some(manifest_deps.clone())
116 } else if dep.unit.pkg.package_id() == root.pkg.package_id() {
117 None
118 } else {
119 continue;
120 };
121 state.externs.insert(
122 dep.extern_crate_name,
123 ExternState {
124 unit: dep.unit.clone(),
125 manifest_deps,
126 },
127 );
128 }
129 }
130
131 Self { states }
132 }
133
134 pub fn record_unused_externs_for_unit(
135 &mut self,
136 unit: &Unit,
137 unused_externs: BTreeSet<InternedString>,
138 ) {
139 let pkg_id = unit.pkg.package_id();
140 let dep_kind = dep_kind_of(unit);
141 trace!(
142 "pkg {} v{} ({dep_kind:?}): unused externs {unused_externs:?}",
143 pkg_id.name(),
144 pkg_id.version(),
145 );
146 let state = self
147 .states
148 .entry(pkg_id)
149 .or_default()
150 .entry(dep_kind)
151 .or_default();
152 state.seen_units.push(unit.clone());
153 if let Some(existing) = state.unused_externs.as_mut() {
154 existing.retain(|ext| unused_externs.contains(ext));
155 } else {
156 state.unused_externs = Some(unused_externs);
157 }
158 }
159}
160
161#[derive(Default)]
163pub struct DependenciesState {
164 pub externs: IndexMap<InternedString, ExternState>,
166 pub needed_units: usize,
171 pub seen_units: Vec<Unit>,
173 pub unused_externs: Option<BTreeSet<InternedString>>,
175}
176
177#[derive(Clone)]
178pub struct ExternState {
179 pub unit: Unit,
180 pub manifest_deps: Option<Vec<Dependency>>,
181}
182
183fn dep_kind_of(unit: &Unit) -> DepKind {
184 match unit.target.kind() {
185 TargetKind::Lib(_) => match unit.mode {
186 CompileMode::Test => DepKind::Development,
188 _ => DepKind::Normal,
189 },
190 TargetKind::Bin => DepKind::Normal,
191 TargetKind::Test => DepKind::Development,
192 TargetKind::Bench => DepKind::Development,
193 TargetKind::ExampleLib(_) => DepKind::Development,
194 TargetKind::ExampleBin => DepKind::Development,
195 TargetKind::CustomBuild => DepKind::Build,
196 }
197}
198
199fn unit_desc(unit: &Unit) -> String {
200 format!(
201 "{}/{}+{:?}",
202 unit.target.name(),
203 unit.target.kind().description(),
204 unit.mode,
205 )
206}