]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/cargo/feature_name.rs
Rollup merge of #102811 - the8472:bufread-memset, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_lints / src / cargo / feature_name.rs
1 use cargo_metadata::Metadata;
2 use clippy_utils::diagnostics::span_lint_and_help;
3 use rustc_lint::LateContext;
4 use rustc_span::source_map::DUMMY_SP;
5
6 use super::{NEGATIVE_FEATURE_NAMES, REDUNDANT_FEATURE_NAMES};
7
8 static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
9 static SUFFIXES: [&str; 2] = ["-support", "_support"];
10
11 pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
12     for package in &metadata.packages {
13         let mut features: Vec<&String> = package.features.keys().collect();
14         features.sort();
15         for feature in features {
16             let prefix_opt = {
17                 let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
18                 if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
19                     Some(PREFIXES[i - 1])
20                 } else {
21                     None
22                 }
23             };
24             if let Some(prefix) = prefix_opt {
25                 lint(cx, feature, prefix, true);
26             }
27
28             let suffix_opt: Option<&str> = {
29                 let i = SUFFIXES.partition_point(|suffix| {
30                     suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
31                 });
32                 if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
33                     Some(SUFFIXES[i - 1])
34                 } else {
35                     None
36                 }
37             };
38             if let Some(suffix) = suffix_opt {
39                 lint(cx, feature, suffix, false);
40             }
41         }
42     }
43 }
44
45 fn is_negative_prefix(s: &str) -> bool {
46     s.starts_with("no")
47 }
48
49 fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
50     let is_negative = is_prefix && is_negative_prefix(substring);
51     span_lint_and_help(
52         cx,
53         if is_negative {
54             NEGATIVE_FEATURE_NAMES
55         } else {
56             REDUNDANT_FEATURE_NAMES
57         },
58         DUMMY_SP,
59         &format!(
60             "the \"{substring}\" {} in the feature name \"{feature}\" is {}",
61             if is_prefix { "prefix" } else { "suffix" },
62             if is_negative { "negative" } else { "redundant" }
63         ),
64         None,
65         &format!(
66             "consider renaming the feature to \"{}\"{}",
67             if is_prefix {
68                 feature.strip_prefix(substring)
69             } else {
70                 feature.strip_suffix(substring)
71             }
72             .unwrap(),
73             if is_negative {
74                 ", but make sure the feature adds functionality"
75             } else {
76                 ""
77             }
78         ),
79     );
80 }
81
82 #[test]
83 fn test_prefixes_sorted() {
84     let mut sorted_prefixes = PREFIXES;
85     sorted_prefixes.sort_unstable();
86     assert_eq!(PREFIXES, sorted_prefixes);
87     let mut sorted_suffixes = SUFFIXES;
88     sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
89     assert_eq!(SUFFIXES, sorted_suffixes);
90 }