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