cargo/diagnostics/rules/
blanket_hint_mostly_unused.rs1use std::path::Path;
2
3use cargo_util_schemas::manifest::ProfilePackageSpec;
4use cargo_util_terminal::report::AnnotationKind;
5use cargo_util_terminal::report::Group;
6use cargo_util_terminal::report::Level;
7use cargo_util_terminal::report::Origin;
8use cargo_util_terminal::report::Patch;
9use cargo_util_terminal::report::Snippet;
10use tracing::instrument;
11
12use super::SUSPICIOUS;
13use crate::CargoResult;
14use crate::GlobalContext;
15use crate::core::MaybePackage;
16use crate::core::Workspace;
17use crate::diagnostics::DiagnosticStats;
18use crate::diagnostics::Lint;
19use crate::diagnostics::LintLevelProduct;
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(crate) fn lint_workspace(
60 _ws: &Workspace<'_>,
61 maybe_pkg: &MaybePackage,
62 path: &Path,
63 level: LintLevelProduct,
64 stats: &mut DiagnosticStats,
65 gctx: &GlobalContext,
66) -> CargoResult<()> {
67 let LintLevelProduct {
68 level: lint_level,
69 source,
70 } = level;
71
72 let level = lint_level.to_diagnostic_level();
73 let manifest_path = rel_cwd_manifest_path(path, gctx);
74 let mut paths = Vec::new();
75
76 if let Some(profiles) = maybe_pkg.profiles() {
77 for (profile_name, top_level_profile) in &profiles.0 {
78 if let Some(true) = top_level_profile.hint_mostly_unused {
79 paths.push((
80 vec!["profile", profile_name.as_str(), "hint-mostly-unused"],
81 true,
82 ));
83 }
84
85 if let Some(build_override) = &top_level_profile.build_override
86 && let Some(true) = build_override.hint_mostly_unused
87 {
88 paths.push((
89 vec![
90 "profile",
91 profile_name.as_str(),
92 "build-override",
93 "hint-mostly-unused",
94 ],
95 false,
96 ));
97 }
98
99 if let Some(packages) = &top_level_profile.package
100 && let Some(profile) = packages.get(&ProfilePackageSpec::All)
101 && let Some(true) = profile.hint_mostly_unused
102 {
103 paths.push((
104 vec![
105 "profile",
106 profile_name.as_str(),
107 "package",
108 "*",
109 "hint-mostly-unused",
110 ],
111 false,
112 ));
113 }
114 }
115 }
116
117 for (i, (path, show_per_pkg_suggestion)) in paths.iter().enumerate() {
118 let title = "`hint-mostly-unused` is being blanket applied to all dependencies";
119 let help_txt =
120 "scope `hint-mostly-unused` to specific packages with a lot of unused object code";
121
122 let mut report = Vec::new();
123 let mut primary_group = Group::with_title(level.clone().primary_title(title));
124
125 if let Some(contents) = maybe_pkg.contents()
126 && let Some(document) = maybe_pkg.document()
127 && let Some(span) = get_key_value_span(document, &path)
128 && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
129 {
130 primary_group = primary_group.element(
131 Snippet::source(contents)
132 .path(&manifest_path)
133 .annotation(
134 AnnotationKind::Primary.span(table_span.key.start..table_span.key.end),
135 )
136 .annotation(AnnotationKind::Context.span(span.key.start..span.value.end)),
137 );
138 } else {
139 primary_group = primary_group.element(Origin::path(&manifest_path))
140 }
141
142 if *show_per_pkg_suggestion {
143 let help_group = Group::with_title(Level::HELP.secondary_title(help_txt));
144
145 report.push(
146 if let Some(contents) = maybe_pkg.contents()
147 && let Some(document) = maybe_pkg.document()
148 && let Some(table_span) = get_key_value_span(document, &path[..path.len() - 1])
149 {
150 help_group.element(Snippet::source(contents).path(&manifest_path).patch(
151 Patch::new(
152 table_span.key.end..table_span.key.end,
153 ".package.<pkg_name>",
154 ),
155 ))
156 } else {
157 help_group.element(Origin::path(&manifest_path))
158 },
159 );
160 } else {
161 primary_group = primary_group.element(Level::HELP.message(help_txt));
162 }
163
164 if i == 0 {
165 primary_group =
166 primary_group.element(Level::NOTE.message(LINT.emitted_source(lint_level, source)));
167 }
168
169 report.insert(0, primary_group);
171
172 stats.record_lint(lint_level);
173 gctx.shell().print_report(&report, lint_level.force())?;
174 }
175
176 Ok(())
177}