1mod blanket_hint_mostly_unused;
2mod im_a_teapot;
3mod implicit_minimum_version_req;
4mod missing_lints_features;
5mod missing_lints_inheritance;
6mod non_kebab_case_bins;
7mod non_kebab_case_features;
8mod non_kebab_case_packages;
9mod non_snake_case_features;
10mod non_snake_case_packages;
11mod redundant_homepage;
12mod redundant_readme;
13mod text_direction_codepoint_in_comment;
14mod text_direction_codepoint_in_literal;
15mod unknown_lints;
16pub mod unused_dependencies;
17mod unused_workspace_dependencies;
18mod unused_workspace_package_fields;
19
20pub use blanket_hint_mostly_unused::blanket_hint_mostly_unused;
21pub use im_a_teapot::check_im_a_teapot;
22pub use implicit_minimum_version_req::implicit_minimum_version_req_pkg;
23pub use implicit_minimum_version_req::implicit_minimum_version_req_ws;
24pub use missing_lints_features::missing_lints_features;
25pub use missing_lints_inheritance::missing_lints_inheritance;
26pub use non_kebab_case_bins::non_kebab_case_bins;
27pub use non_kebab_case_features::non_kebab_case_features;
28pub use non_kebab_case_packages::non_kebab_case_packages;
29pub use non_snake_case_features::non_snake_case_features;
30pub use non_snake_case_packages::non_snake_case_packages;
31pub use redundant_homepage::redundant_homepage;
32pub use redundant_readme::redundant_readme;
33pub use text_direction_codepoint_in_comment::text_direction_codepoint_in_comment;
34pub use text_direction_codepoint_in_literal::text_direction_codepoint_in_literal;
35pub use unknown_lints::unknown_lints;
36pub use unused_dependencies::unused_build_dependencies_no_build_rs;
37pub use unused_workspace_dependencies::unused_workspace_dependencies;
38pub use unused_workspace_package_fields::unused_workspace_package_fields;
39
40use super::LintGroup;
41use super::LintLevel;
42use crate::core::Feature;
43
44pub static LINTS: &[&crate::diagnostics::Lint] = &[
45 blanket_hint_mostly_unused::LINT,
46 implicit_minimum_version_req::LINT,
47 im_a_teapot::LINT,
48 missing_lints_inheritance::LINT,
49 non_kebab_case_bins::LINT,
50 non_kebab_case_features::LINT,
51 non_kebab_case_packages::LINT,
52 non_snake_case_features::LINT,
53 non_snake_case_packages::LINT,
54 redundant_homepage::LINT,
55 redundant_readme::LINT,
56 text_direction_codepoint_in_comment::LINT,
57 text_direction_codepoint_in_literal::LINT,
58 unknown_lints::LINT,
59 unused_dependencies::LINT,
60 unused_workspace_dependencies::LINT,
61 unused_workspace_package_fields::LINT,
62];
63
64static CARGO_LINTS_MSRV: cargo_util_schemas::manifest::RustVersion =
69 cargo_util_schemas::manifest::RustVersion::new(1, 79, 0);
70
71pub static LINT_GROUPS: &[LintGroup] = &[
72 COMPLEXITY,
73 CORRECTNESS,
74 NURSERY,
75 PEDANTIC,
76 PERF,
77 RESTRICTION,
78 STYLE,
79 SUSPICIOUS,
80 TEST_DUMMY_UNSTABLE,
81];
82
83const COMPLEXITY: LintGroup = LintGroup {
84 name: "complexity",
85 desc: "code that does something simple but in a complex way",
86 default_level: LintLevel::Warn,
87 feature_gate: None,
88 hidden: false,
89};
90
91const CORRECTNESS: LintGroup = LintGroup {
92 name: "correctness",
93 desc: "code that is outright wrong or useless",
94 default_level: LintLevel::Deny,
95 feature_gate: None,
96 hidden: false,
97};
98
99const NURSERY: LintGroup = LintGroup {
100 name: "nursery",
101 desc: "new lints that are still under development",
102 default_level: LintLevel::Allow,
103 feature_gate: None,
104 hidden: false,
105};
106
107const PEDANTIC: LintGroup = LintGroup {
108 name: "pedantic",
109 desc: "lints which are rather strict or have occasional false positives",
110 default_level: LintLevel::Allow,
111 feature_gate: None,
112 hidden: false,
113};
114
115const PERF: LintGroup = LintGroup {
116 name: "perf",
117 desc: "code that can be written to run faster",
118 default_level: LintLevel::Warn,
119 feature_gate: None,
120 hidden: false,
121};
122
123const RESTRICTION: LintGroup = LintGroup {
124 name: "restriction",
125 desc: "lints which prevent the use of Cargo features",
126 default_level: LintLevel::Allow,
127 feature_gate: None,
128 hidden: false,
129};
130
131const STYLE: LintGroup = LintGroup {
132 name: "style",
133 desc: "code that should be written in a more idiomatic way",
134 default_level: LintLevel::Warn,
135 feature_gate: None,
136 hidden: false,
137};
138
139const SUSPICIOUS: LintGroup = LintGroup {
140 name: "suspicious",
141 desc: "code that is most likely wrong or useless",
142 default_level: LintLevel::Warn,
143 feature_gate: None,
144 hidden: false,
145};
146
147const TEST_DUMMY_UNSTABLE: LintGroup = LintGroup {
149 name: "test_dummy_unstable",
150 desc: "test_dummy_unstable is meant to only be used in tests",
151 default_level: LintLevel::Allow,
152 feature_gate: Some(crate::core::Feature::test_dummy_unstable()),
153 hidden: true,
154};
155
156fn find_lint_or_group<'a>(
157 name: &str,
158) -> Option<(&'static str, &LintLevel, &Option<&'static Feature>)> {
159 if let Some(lint) = LINTS.iter().find(|l| l.name == name) {
160 Some((
161 lint.name,
162 &lint.primary_group.default_level,
163 &lint.feature_gate,
164 ))
165 } else if let Some(group) = LINT_GROUPS.iter().find(|g| g.name == name) {
166 Some((group.name, &group.default_level, &group.feature_gate))
167 } else {
168 None
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use itertools::Itertools;
175 use snapbox::ToDebug;
176 use std::collections::HashSet;
177
178 #[test]
179 fn ensure_lint_groups_do_not_default_to_forbid() {
180 let forbid_groups = super::LINT_GROUPS
181 .iter()
182 .filter(|g| matches!(g.default_level, super::LintLevel::Forbid))
183 .collect::<Vec<_>>();
184
185 assert!(
186 forbid_groups.is_empty(),
187 "\n`LintGroup`s should never default to `forbid`, but the following do:\n\
188 {}\n",
189 forbid_groups.iter().map(|g| g.name).join("\n")
190 );
191 }
192
193 #[test]
194 fn ensure_sorted_lints() {
195 let location = std::panic::Location::caller();
197 println!("\nTo fix this test, sort `LINTS` in {}\n", location.file(),);
198
199 let actual = super::LINTS
200 .iter()
201 .map(|l| l.name.to_uppercase())
202 .collect::<Vec<_>>();
203
204 let mut expected = actual.clone();
205 expected.sort();
206 snapbox::assert_data_eq!(actual.to_debug(), expected.to_debug());
207 }
208
209 #[test]
210 fn ensure_sorted_lint_groups() {
211 let location = std::panic::Location::caller();
213 println!(
214 "\nTo fix this test, sort `LINT_GROUPS` in {}\n",
215 location.file(),
216 );
217 let actual = super::LINT_GROUPS
218 .iter()
219 .map(|l| l.name.to_uppercase())
220 .collect::<Vec<_>>();
221
222 let mut expected = actual.clone();
223 expected.sort();
224 snapbox::assert_data_eq!(actual.to_debug(), expected.to_debug());
225 }
226
227 #[test]
228 fn ensure_updated_lints() {
229 let dir = snapbox::utils::current_dir!();
230 let mut expected = HashSet::new();
231 for entry in std::fs::read_dir(&dir).unwrap() {
232 let entry = entry.unwrap();
233 let path = entry.path();
234 if path.ends_with("mod.rs") {
235 continue;
236 }
237 let content = std::fs::read_to_string(&path).unwrap();
238 if !content.contains("LINT") {
239 continue;
241 }
242 let lint_name = path.file_stem().unwrap().to_string_lossy();
243 assert!(expected.insert(lint_name.into()), "duplicate lint found");
244 }
245
246 let actual = super::LINTS
247 .iter()
248 .map(|l| l.name.to_string())
249 .collect::<HashSet<_>>();
250 let diff = expected.difference(&actual).sorted().collect::<Vec<_>>();
251
252 let mut need_added = String::new();
253 for name in &diff {
254 need_added.push_str(&format!("{name}\n"));
255 }
256 assert!(
257 diff.is_empty(),
258 "\n`LINTS` did not contain all `Lint`s found in {}\n\
259 Please add the following to `LINTS`:\n\
260 {need_added}",
261 dir.display(),
262 );
263 }
264
265 #[test]
266 fn ensure_updated_lint_groups() {
267 let path = snapbox::utils::current_rs!();
268 let expected = std::fs::read_to_string(&path).unwrap();
269 let expected = expected
270 .lines()
271 .filter_map(|l| {
272 if l.ends_with(": LintGroup = LintGroup {") {
273 Some(
274 l.chars()
275 .skip(6)
276 .take_while(|c| *c != ':')
277 .collect::<String>(),
278 )
279 } else {
280 None
281 }
282 })
283 .collect::<HashSet<_>>();
284 let actual = super::LINT_GROUPS
285 .iter()
286 .map(|l| l.name.to_uppercase())
287 .collect::<HashSet<_>>();
288 let diff = expected.difference(&actual).sorted().collect::<Vec<_>>();
289
290 let mut need_added = String::new();
291 for name in &diff {
292 need_added.push_str(&format!("{}\n", name));
293 }
294 assert!(
295 diff.is_empty(),
296 "\n`LINT_GROUPS` did not contain all `LintGroup`s found in {}\n\
297 Please add the following to `LINT_GROUPS`:\n\
298 {}",
299 path.display(),
300 need_added
301 );
302 }
303}