1 use clippy_utils::diagnostics::span_lint_and_help;
2 use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
3 use rustc_hir::CRATE_HIR_ID;
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6 use rustc_span::source_map::DUMMY_SP;
10 /// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
12 /// ### Why is this bad?
13 /// These prefixes and suffixes have no significant meaning.
17 /// # The `Cargo.toml` with feature name redundancy
19 /// default = ["use-abc", "with-def", "ghi-support"]
20 /// use-abc = [] // redundant
21 /// with-def = [] // redundant
22 /// ghi-support = [] // redundant
28 /// default = ["abc", "def", "ghi"]
34 #[clippy::version = "1.57.0"]
35 pub REDUNDANT_FEATURE_NAMES,
37 "usage of a redundant feature name"
40 declare_clippy_lint! {
42 /// Checks for negative feature names with prefix `no-` or `not-`
44 /// ### Why is this bad?
45 /// Features are supposed to be additive, and negatively-named features violate it.
49 /// # The `Cargo.toml` with negative feature names
59 /// default = ["abc", "def"]
64 #[clippy::version = "1.57.0"]
65 pub NEGATIVE_FEATURE_NAMES,
67 "usage of a negative feature name"
70 declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]);
72 static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
73 static SUFFIXES: [&str; 2] = ["-support", "_support"];
75 fn is_negative_prefix(s: &str) -> bool {
79 fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
80 let is_negative = is_prefix && is_negative_prefix(substring);
84 NEGATIVE_FEATURE_NAMES
86 REDUNDANT_FEATURE_NAMES
90 "the \"{}\" {} in the feature name \"{}\" is {}",
92 if is_prefix { "prefix" } else { "suffix" },
94 if is_negative { "negative" } else { "redundant" }
98 "consider renaming the feature to \"{}\"{}",
100 feature.strip_prefix(substring)
102 feature.strip_suffix(substring)
106 ", but make sure the feature adds functionality"
114 impl LateLintPass<'_> for FeatureName {
115 fn check_crate(&mut self, cx: &LateContext<'_>) {
116 if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID)
117 && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID)
122 let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false);
124 for package in metadata.packages {
125 let mut features: Vec<&String> = package.features.keys().collect();
127 for feature in features {
129 let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
130 if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
131 Some(PREFIXES[i - 1])
136 if let Some(prefix) = prefix_opt {
137 lint(cx, feature, prefix, true);
140 let suffix_opt: Option<&str> = {
141 let i = SUFFIXES.partition_point(|suffix| {
142 suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
144 if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
145 Some(SUFFIXES[i - 1])
150 if let Some(suffix) = suffix_opt {
151 lint(cx, feature, suffix, false);
159 fn test_prefixes_sorted() {
160 let mut sorted_prefixes = PREFIXES;
161 sorted_prefixes.sort_unstable();
162 assert_eq!(PREFIXES, sorted_prefixes);
163 let mut sorted_suffixes = SUFFIXES;
164 sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
165 assert_eq!(SUFFIXES, sorted_suffixes);