about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-08-23 13:56:18 +0000
committerbors <bors@rust-lang.org>2021-08-23 13:56:18 +0000
commit22606e7358845eb8ddbc37243e89914c03084afb (patch)
treee1f9ee28e075d7b180cc5e74ae50f80b4461effa
parent1fc1aee7be6e7e71f12eacd60b9b1040e7640fe4 (diff)
parent0a021d5900a9ee55875fb247614a0671b71d3271 (diff)
downloadrust-22606e7358845eb8ddbc37243e89914c03084afb.tar.gz
rust-22606e7358845eb8ddbc37243e89914c03084afb.zip
Auto merge of #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`]
-rw-r--r--CHANGELOG.md2
-rw-r--r--clippy_lints/src/feature_name.rs164
-rw-r--r--clippy_lints/src/lib.rs6
-rw-r--r--tests/ui-cargo/feature_name/fail/Cargo.toml21
-rw-r--r--tests/ui-cargo/feature_name/fail/src/main.rs7
-rw-r--r--tests/ui-cargo/feature_name/fail/src/main.stderr44
-rw-r--r--tests/ui-cargo/feature_name/pass/Cargo.toml9
-rw-r--r--tests/ui-cargo/feature_name/pass/src/main.rs7
-rw-r--r--tests/ui/missing-doc-crate.stderr0
-rw-r--r--tests/ui/missing_const_for_fn/cant_be_const.stderr0
-rw-r--r--tests/ui/rc_buffer_redefined_string.stderr0
11 files changed, 260 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9e67d361c0..2b645e82b8e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2829,6 +2829,7 @@ Released 2018-09-13
 [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
+[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 [`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
@@ -2882,6 +2883,7 @@ Released 2018-09-13
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
+[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
diff --git a/clippy_lints/src/feature_name.rs b/clippy_lints/src/feature_name.rs
new file mode 100644
index 00000000000..eef1407a80c
--- /dev/null
+++ b/clippy_lints/src/feature_name.rs
@@ -0,0 +1,164 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
+use rustc_hir::{Crate, CRATE_HIR_ID};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::DUMMY_SP;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
+    ///
+    /// ### Why is this bad?
+    /// These prefixes and suffixes have no significant meaning.
+    ///
+    /// ### Example
+    /// ```toml
+    /// # The `Cargo.toml` with feature name redundancy
+    /// [features]
+    /// default = ["use-abc", "with-def", "ghi-support"]
+    /// use-abc = []  // redundant
+    /// with-def = []   // redundant
+    /// ghi-support = []   // redundant
+    /// ```
+    ///
+    /// Use instead:
+    /// ```toml
+    /// [features]
+    /// default = ["abc", "def", "ghi"]
+    /// abc = []
+    /// def = []
+    /// ghi = []
+    /// ```
+    ///
+    pub REDUNDANT_FEATURE_NAMES,
+    cargo,
+    "usage of a redundant feature name"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for negative feature names with prefix `no-` or `not-`
+    ///
+    /// ### Why is this bad?
+    /// Features are supposed to be additive, and negatively-named features violate it.
+    ///
+    /// ### Example
+    /// ```toml
+    /// # The `Cargo.toml` with negative feature names
+    /// [features]
+    /// default = []
+    /// no-abc = []
+    /// not-def = []
+    ///
+    /// ```
+    /// Use instead:
+    /// ```toml
+    /// [features]
+    /// default = ["abc", "def"]
+    /// abc = []
+    /// def = []
+    ///
+    /// ```
+    pub NEGATIVE_FEATURE_NAMES,
+    cargo,
+    "usage of a negative feature name"
+}
+
+declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]);
+
+static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
+static SUFFIXES: [&str; 2] = ["-support", "_support"];
+
+fn is_negative_prefix(s: &str) -> bool {
+    s.starts_with("no")
+}
+
+fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
+    let is_negative = is_prefix && is_negative_prefix(substring);
+    span_lint_and_help(
+        cx,
+        if is_negative {
+            NEGATIVE_FEATURE_NAMES
+        } else {
+            REDUNDANT_FEATURE_NAMES
+        },
+        DUMMY_SP,
+        &format!(
+            "the \"{}\" {} in the feature name \"{}\" is {}",
+            substring,
+            if is_prefix { "prefix" } else { "suffix" },
+            feature,
+            if is_negative { "negative" } else { "redundant" }
+        ),
+        None,
+        &format!(
+            "consider renaming the feature to \"{}\"{}",
+            if is_prefix {
+                feature.strip_prefix(substring)
+            } else {
+                feature.strip_suffix(substring)
+            }
+            .unwrap(),
+            if is_negative {
+                ", but make sure the feature adds functionality"
+            } else {
+                ""
+            }
+        ),
+    );
+}
+
+impl LateLintPass<'_> for FeatureName {
+    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+        if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID)
+            && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID)
+        {
+            return;
+        }
+
+        let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false);
+
+        for package in metadata.packages {
+            let mut features: Vec<&String> = package.features.keys().collect();
+            features.sort();
+            for feature in features {
+                let prefix_opt = {
+                    let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
+                    if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
+                        Some(PREFIXES[i - 1])
+                    } else {
+                        None
+                    }
+                };
+                if let Some(prefix) = prefix_opt {
+                    lint(cx, feature, prefix, true);
+                }
+
+                let suffix_opt: Option<&str> = {
+                    let i = SUFFIXES.partition_point(|suffix| {
+                        suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
+                    });
+                    if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
+                        Some(SUFFIXES[i - 1])
+                    } else {
+                        None
+                    }
+                };
+                if let Some(suffix) = suffix_opt {
+                    lint(cx, feature, suffix, false);
+                }
+            }
+        }
+    }
+}
+
+#[test]
+fn test_prefixes_sorted() {
+    let mut sorted_prefixes = PREFIXES;
+    sorted_prefixes.sort_unstable();
+    assert_eq!(PREFIXES, sorted_prefixes);
+    let mut sorted_suffixes = SUFFIXES;
+    sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
+    assert_eq!(SUFFIXES, sorted_suffixes);
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 1fadaf4770a..c98803adbed 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -213,6 +213,7 @@ mod exhaustive_items;
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
+mod feature_name;
 mod float_equality_without_abs;
 mod float_literal;
 mod floating_point_arithmetic;
@@ -628,6 +629,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         exit::EXIT,
         explicit_write::EXPLICIT_WRITE,
         fallible_impl_from::FALLIBLE_IMPL_FROM,
+        feature_name::NEGATIVE_FEATURE_NAMES,
+        feature_name::REDUNDANT_FEATURE_NAMES,
         float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
         float_literal::EXCESSIVE_PRECISION,
         float_literal::LOSSY_FLOAT_LITERAL,
@@ -1786,6 +1789,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
         LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
+        LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
+        LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
         LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
         LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
     ]);
@@ -2116,6 +2121,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
     store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings);
     store.register_late_pass(move || box self_named_constructors::SelfNamedConstructors);
+    store.register_late_pass(move || box feature_name::FeatureName);
 }
 
 #[rustfmt::skip]
diff --git a/tests/ui-cargo/feature_name/fail/Cargo.toml b/tests/ui-cargo/feature_name/fail/Cargo.toml
new file mode 100644
index 00000000000..97d51462a94
--- /dev/null
+++ b/tests/ui-cargo/feature_name/fail/Cargo.toml
@@ -0,0 +1,21 @@
+
+# Content that triggers the lint goes here
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[features]
+use-qwq = []
+use_qwq = []
+with-owo = []
+with_owo = []
+qvq-support = []
+qvq_support = []
+no-qaq = []
+no_qaq = []
+not-orz = []
+not_orz = []
diff --git a/tests/ui-cargo/feature_name/fail/src/main.rs b/tests/ui-cargo/feature_name/fail/src/main.rs
new file mode 100644
index 00000000000..64f01a98c90
--- /dev/null
+++ b/tests/ui-cargo/feature_name/fail/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+    // test code goes here
+}
diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr
new file mode 100644
index 00000000000..b9e6cb49bc9
--- /dev/null
+++ b/tests/ui-cargo/feature_name/fail/src/main.stderr
@@ -0,0 +1,44 @@
+error: the "no-" prefix in the feature name "no-qaq" is negative
+   |
+   = note: `-D clippy::negative-feature-names` implied by `-D warnings`
+   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "no_" prefix in the feature name "no_qaq" is negative
+   |
+   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "not-" prefix in the feature name "not-orz" is negative
+   |
+   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "not_" prefix in the feature name "not_orz" is negative
+   |
+   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "-support" suffix in the feature name "qvq-support" is redundant
+   |
+   = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
+   = help: consider renaming the feature to "qvq"
+
+error: the "_support" suffix in the feature name "qvq_support" is redundant
+   |
+   = help: consider renaming the feature to "qvq"
+
+error: the "use-" prefix in the feature name "use-qwq" is redundant
+   |
+   = help: consider renaming the feature to "qwq"
+
+error: the "use_" prefix in the feature name "use_qwq" is redundant
+   |
+   = help: consider renaming the feature to "qwq"
+
+error: the "with-" prefix in the feature name "with-owo" is redundant
+   |
+   = help: consider renaming the feature to "owo"
+
+error: the "with_" prefix in the feature name "with_owo" is redundant
+   |
+   = help: consider renaming the feature to "owo"
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui-cargo/feature_name/pass/Cargo.toml b/tests/ui-cargo/feature_name/pass/Cargo.toml
new file mode 100644
index 00000000000..cf947312bd4
--- /dev/null
+++ b/tests/ui-cargo/feature_name/pass/Cargo.toml
@@ -0,0 +1,9 @@
+
+# This file should not trigger the lint
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
diff --git a/tests/ui-cargo/feature_name/pass/src/main.rs b/tests/ui-cargo/feature_name/pass/src/main.rs
new file mode 100644
index 00000000000..64f01a98c90
--- /dev/null
+++ b/tests/ui-cargo/feature_name/pass/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+    // test code goes here
+}
diff --git a/tests/ui/missing-doc-crate.stderr b/tests/ui/missing-doc-crate.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/tests/ui/missing-doc-crate.stderr
+++ /dev/null
diff --git a/tests/ui/missing_const_for_fn/cant_be_const.stderr b/tests/ui/missing_const_for_fn/cant_be_const.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/tests/ui/missing_const_for_fn/cant_be_const.stderr
+++ /dev/null
diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/tests/ui/rc_buffer_redefined_string.stderr
+++ /dev/null