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