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