Skip to content

Commit 22606e7

Browse files
committed
Auto merge of rust-lang#7539 - Labelray:master, r=camsteffen
Add new lint `negative_feature_names` and `redundant_feature_names` Add new lint [`negative_feature_names`] to detect feature names with prefixes `no-` or `not-` and new lint [`redundant_feature_names`] to detect feature names with prefixes `use-`, `with-` or suffix `-support` changelog: Add new lint [`negative_feature_names`] and [`redundant_feature_names`]
2 parents 1fc1aee + 0a021d5 commit 22606e7

File tree

11 files changed

+260
-0
lines changed

11 files changed

+260
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,7 @@ Released 2018-09-13
28292829
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
28302830
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
28312831
[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
2832+
[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
28322833
[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
28332834
[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
28342835
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
@@ -2882,6 +2883,7 @@ Released 2018-09-13
28822883
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
28832884
[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
28842885
[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
2886+
[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
28852887
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
28862888
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
28872889
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching

clippy_lints/src/feature_name.rs

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
3+
use rustc_hir::{Crate, 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;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
11+
///
12+
/// ### Why is this bad?
13+
/// These prefixes and suffixes have no significant meaning.
14+
///
15+
/// ### Example
16+
/// ```toml
17+
/// # The `Cargo.toml` with feature name redundancy
18+
/// [features]
19+
/// default = ["use-abc", "with-def", "ghi-support"]
20+
/// use-abc = [] // redundant
21+
/// with-def = [] // redundant
22+
/// ghi-support = [] // redundant
23+
/// ```
24+
///
25+
/// Use instead:
26+
/// ```toml
27+
/// [features]
28+
/// default = ["abc", "def", "ghi"]
29+
/// abc = []
30+
/// def = []
31+
/// ghi = []
32+
/// ```
33+
///
34+
pub REDUNDANT_FEATURE_NAMES,
35+
cargo,
36+
"usage of a redundant feature name"
37+
}
38+
39+
declare_clippy_lint! {
40+
/// ### What it does
41+
/// Checks for negative feature names with prefix `no-` or `not-`
42+
///
43+
/// ### Why is this bad?
44+
/// Features are supposed to be additive, and negatively-named features violate it.
45+
///
46+
/// ### Example
47+
/// ```toml
48+
/// # The `Cargo.toml` with negative feature names
49+
/// [features]
50+
/// default = []
51+
/// no-abc = []
52+
/// not-def = []
53+
///
54+
/// ```
55+
/// Use instead:
56+
/// ```toml
57+
/// [features]
58+
/// default = ["abc", "def"]
59+
/// abc = []
60+
/// def = []
61+
///
62+
/// ```
63+
pub NEGATIVE_FEATURE_NAMES,
64+
cargo,
65+
"usage of a negative feature name"
66+
}
67+
68+
declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]);
69+
70+
static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
71+
static SUFFIXES: [&str; 2] = ["-support", "_support"];
72+
73+
fn is_negative_prefix(s: &str) -> bool {
74+
s.starts_with("no")
75+
}
76+
77+
fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
78+
let is_negative = is_prefix && is_negative_prefix(substring);
79+
span_lint_and_help(
80+
cx,
81+
if is_negative {
82+
NEGATIVE_FEATURE_NAMES
83+
} else {
84+
REDUNDANT_FEATURE_NAMES
85+
},
86+
DUMMY_SP,
87+
&format!(
88+
"the \"{}\" {} in the feature name \"{}\" is {}",
89+
substring,
90+
if is_prefix { "prefix" } else { "suffix" },
91+
feature,
92+
if is_negative { "negative" } else { "redundant" }
93+
),
94+
None,
95+
&format!(
96+
"consider renaming the feature to \"{}\"{}",
97+
if is_prefix {
98+
feature.strip_prefix(substring)
99+
} else {
100+
feature.strip_suffix(substring)
101+
}
102+
.unwrap(),
103+
if is_negative {
104+
", but make sure the feature adds functionality"
105+
} else {
106+
""
107+
}
108+
),
109+
);
110+
}
111+
112+
impl LateLintPass<'_> for FeatureName {
113+
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
114+
if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID)
115+
&& is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID)
116+
{
117+
return;
118+
}
119+
120+
let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false);
121+
122+
for package in metadata.packages {
123+
let mut features: Vec<&String> = package.features.keys().collect();
124+
features.sort();
125+
for feature in features {
126+
let prefix_opt = {
127+
let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
128+
if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
129+
Some(PREFIXES[i - 1])
130+
} else {
131+
None
132+
}
133+
};
134+
if let Some(prefix) = prefix_opt {
135+
lint(cx, feature, prefix, true);
136+
}
137+
138+
let suffix_opt: Option<&str> = {
139+
let i = SUFFIXES.partition_point(|suffix| {
140+
suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
141+
});
142+
if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
143+
Some(SUFFIXES[i - 1])
144+
} else {
145+
None
146+
}
147+
};
148+
if let Some(suffix) = suffix_opt {
149+
lint(cx, feature, suffix, false);
150+
}
151+
}
152+
}
153+
}
154+
}
155+
156+
#[test]
157+
fn test_prefixes_sorted() {
158+
let mut sorted_prefixes = PREFIXES;
159+
sorted_prefixes.sort_unstable();
160+
assert_eq!(PREFIXES, sorted_prefixes);
161+
let mut sorted_suffixes = SUFFIXES;
162+
sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
163+
assert_eq!(SUFFIXES, sorted_suffixes);
164+
}

clippy_lints/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ mod exhaustive_items;
213213
mod exit;
214214
mod explicit_write;
215215
mod fallible_impl_from;
216+
mod feature_name;
216217
mod float_equality_without_abs;
217218
mod float_literal;
218219
mod floating_point_arithmetic;
@@ -628,6 +629,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
628629
exit::EXIT,
629630
explicit_write::EXPLICIT_WRITE,
630631
fallible_impl_from::FALLIBLE_IMPL_FROM,
632+
feature_name::NEGATIVE_FEATURE_NAMES,
633+
feature_name::REDUNDANT_FEATURE_NAMES,
631634
float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
632635
float_literal::EXCESSIVE_PRECISION,
633636
float_literal::LOSSY_FLOAT_LITERAL,
@@ -1786,6 +1789,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17861789

17871790
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
17881791
LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
1792+
LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
1793+
LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
17891794
LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
17901795
LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
17911796
]);
@@ -2116,6 +2121,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
21162121
store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
21172122
store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings);
21182123
store.register_late_pass(move || box self_named_constructors::SelfNamedConstructors);
2124+
store.register_late_pass(move || box feature_name::FeatureName);
21192125
}
21202126

21212127
#[rustfmt::skip]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
# Content that triggers the lint goes here
3+
4+
[package]
5+
name = "feature_name"
6+
version = "0.1.0"
7+
publish = false
8+
9+
[workspace]
10+
11+
[features]
12+
use-qwq = []
13+
use_qwq = []
14+
with-owo = []
15+
with_owo = []
16+
qvq-support = []
17+
qvq_support = []
18+
no-qaq = []
19+
no_qaq = []
20+
not-orz = []
21+
not_orz = []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// compile-flags: --crate-name=feature_name
2+
#![warn(clippy::redundant_feature_names)]
3+
#![warn(clippy::negative_feature_names)]
4+
5+
fn main() {
6+
// test code goes here
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: the "no-" prefix in the feature name "no-qaq" is negative
2+
|
3+
= note: `-D clippy::negative-feature-names` implied by `-D warnings`
4+
= help: consider renaming the feature to "qaq", but make sure the feature adds functionality
5+
6+
error: the "no_" prefix in the feature name "no_qaq" is negative
7+
|
8+
= help: consider renaming the feature to "qaq", but make sure the feature adds functionality
9+
10+
error: the "not-" prefix in the feature name "not-orz" is negative
11+
|
12+
= help: consider renaming the feature to "orz", but make sure the feature adds functionality
13+
14+
error: the "not_" prefix in the feature name "not_orz" is negative
15+
|
16+
= help: consider renaming the feature to "orz", but make sure the feature adds functionality
17+
18+
error: the "-support" suffix in the feature name "qvq-support" is redundant
19+
|
20+
= note: `-D clippy::redundant-feature-names` implied by `-D warnings`
21+
= help: consider renaming the feature to "qvq"
22+
23+
error: the "_support" suffix in the feature name "qvq_support" is redundant
24+
|
25+
= help: consider renaming the feature to "qvq"
26+
27+
error: the "use-" prefix in the feature name "use-qwq" is redundant
28+
|
29+
= help: consider renaming the feature to "qwq"
30+
31+
error: the "use_" prefix in the feature name "use_qwq" is redundant
32+
|
33+
= help: consider renaming the feature to "qwq"
34+
35+
error: the "with-" prefix in the feature name "with-owo" is redundant
36+
|
37+
= help: consider renaming the feature to "owo"
38+
39+
error: the "with_" prefix in the feature name "with_owo" is redundant
40+
|
41+
= help: consider renaming the feature to "owo"
42+
43+
error: aborting due to 10 previous errors
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
# This file should not trigger the lint
3+
4+
[package]
5+
name = "feature_name"
6+
version = "0.1.0"
7+
publish = false
8+
9+
[workspace]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// compile-flags: --crate-name=feature_name
2+
#![warn(clippy::redundant_feature_names)]
3+
#![warn(clippy::negative_feature_names)]
4+
5+
fn main() {
6+
// test code goes here
7+
}

tests/ui/missing-doc-crate.stderr

Whitespace-only changes.

tests/ui/missing_const_for_fn/cant_be_const.stderr

Whitespace-only changes.

tests/ui/rc_buffer_redefined_string.stderr

Whitespace-only changes.

0 commit comments

Comments
 (0)