cargo/diagnostics/rules/
blanket_hint_mostly_unused.rs1use std::path::Path;
2
3use cargo_util_schemas::manifest::ProfilePackageSpec;
4use cargo_util_schemas::manifest::TomlToolLints;
5use cargo_util_terminal::report::AnnotationKind;
6use cargo_util_terminal::report::Group;
7use cargo_util_terminal::report::Level;
8use cargo_util_terminal::report::Origin;
9use cargo_util_terminal::report::Patch;
10use cargo_util_terminal::report::Snippet;
11use tracing::instrument;
12
13use super::SUSPICIOUS;
14use crate::CargoResult;
15use crate::GlobalContext;
16use crate::core::MaybePackage;
17use crate::core::Workspace;
18use crate::diagnostics::Lint;
19use crate::diagnostics::LintLevel;
20use crate::diagnostics::get_key_value_span;
21use crate::diagnostics::rel_cwd_manifest_path;
22
23pub static LINT: &Lint = &Lint {
24 name: "blanket_hint_mostly_unused",
25 desc: "blanket_hint_mostly_unused lint",
26 primary_group: &SUSPICIOUS,
27 msrv: Some(super::CARGO_LINTS_MSRV),
28 feature_gate: None,
29 docs: Some(
30 r#"
31### What it does
32Checks if `hint-mostly-unused` being applied to all dependencies.
33
34### Why it is bad
35`hint-mostly-unused` indicates that most of a crate's API surface will go
36unused by anything depending on it; this hint can speed up the build by
37attempting to minimize compilation time for items that aren't used at all.
38Misapplication to crates that don't fit that criteria will slow down the build
39rather than speeding it up. It should be selectively applied to dependencies
40that meet these criteria. Applying it globally is always a misapplication and
41will likely slow down the build.
42
43### Example
44```toml
45[profile.dev.package."*"]
46hint-mostly-unused = true
47```
48
49Should instead be:
50```toml
51[profile.dev.package.huge-mostly-unused-dependency]
52hint-mostly-unused = true
53```
54"#,
55 ),
56};
57
58#[instrument(skip_all)]
59pub fn blanket_hint_mostly_unused(
60 ws: &Workspace<'_>,
61 maybe_pkg: &MaybePackage,
62 path: &Path,
63 pkg_lints: &TomlToolLints,
64 error_count: &mut usize,
65 gctx: &GlobalContext,
66) -> CargoResult<()> {
67 let (lint_level, source) = LINT.level(
68 pkg_lints,
69 ws.lowest_rust_version(),
70 maybe_pkg.unstable_features(),
71 );
72
73 if lint_level == LintLevel::Allow {
74 return Ok(());
75 }
76
77 let level = lint_level.to_diagnostic_level();
78 let manifest_path = rel_cwd_manifest_path(path, gctx);
79 let mut paths = Vec::new();
80
81 if let Some(profiles) = maybe_pkg.profiles() {
82 for (profile_name, top_level_profile) in &profiles.0 {
83 if let Some(true) = top_level_profile.hint_mostly_unused {
84 paths.push((
85 vec!["profile", profile_name.as_str(), "hint-mostly-unused"],
86 true,
87 ));
88 }
89
90 if let Some(build_override) = &top_level_profile.build_override
91 && let Some(true) = build_override.hint_mostly_unused
92 {
93 paths.push((
94 vec![
95 "profile",
96 profile_name.as_str(),
97 "build-override",
98 "hint-mostly-unused",
99 ],
100 false,
101 ));
102 }
103
104 if let Some(packages) = &top_level_profile.package
105 && let Some(profile) = packages.get(&ProfilePackageSpec::All)
106 && let Some(true) = profile.hint_mostly_unused
107 {
108 paths.push((
109 vec![
110 "profile",
111 profile_name.as_str(),
112 "package",
113 "*",
114 "hint-mostly-unused",
115 ],
116 false,
117 ));
118 }
119 }
120 }
121
122 for (i, (path, show_per_pkg_suggestion)) in paths.iter().enumerate() {
123 if lint_level.is_error() {
124 *error_count += 1;
125 }
126 let title = "`hint-mostly-unused` is being blanket applied to all dependencies";
127 let help_txt =
128 "scope `hint-mostly-unused` to specific packages with a lot of unused object code";
129
130 let mut report = Vec::new();
131 let mut primary_group = Group::with_title(level.clone().primary_title(title));
132
133 if let Some(contents) = maybe_pkg.contents()
134 && let Some(document) = maybe_pkg.document()
135 && let Some(span) = get_key_value_span(document, &path)
136 && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
137 {
138 primary_group = primary_group.element(
139 Snippet::source(contents)
140 .path(&manifest_path)
141 .annotation(
142 AnnotationKind::Primary.span(table_span.key.start..table_span.key.end),
143 )
144 .annotation(AnnotationKind::Context.span(span.key.start..span.value.end)),
145 );
146 } else {
147 primary_group = primary_group.element(Origin::path(&manifest_path))
148 }
149
150 if *show_per_pkg_suggestion {
151 let help_group = Group::with_title(Level::HELP.secondary_title(help_txt));
152
153 report.push(
154 if let Some(contents) = maybe_pkg.contents()
155 && let Some(document) = maybe_pkg.document()
156 && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
157 {
158 help_group.element(Snippet::source(contents).path(&manifest_path).patch(
159 Patch::new(
160 table_span.key.end..table_span.key.end,
161 ".package.<pkg_name>",
162 ),
163 ))
164 } else {
165 help_group.element(Origin::path(&manifest_path))
166 },
167 );
168 } else {
169 primary_group = primary_group.element(Level::HELP.message(help_txt));
170 }
171
172 if i == 0 {
173 primary_group =
174 primary_group.element(Level::NOTE.message(LINT.emitted_source(lint_level, source)));
175 }
176
177 report.insert(0, primary_group);
179
180 gctx.shell().print_report(&report, lint_level.force())?;
181 }
182
183 Ok(())
184}