about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-29 11:44:51 +0000
committerbors <bors@rust-lang.org>2024-02-29 11:44:51 +0000
commit00ff8c92d395d08325301c1a1462fc38abd82053 (patch)
tree8c75cecda0dbfa7ff8d475cd4b26ac51d53424c4
parent04b74ee392bec1031a386e0ac8b7e22d76487da1 (diff)
parentf30138623bd9a078698328a32785780cee379e8d (diff)
downloadrust-00ff8c92d395d08325301c1a1462fc38abd82053.tar.gz
rust-00ff8c92d395d08325301c1a1462fc38abd82053.zip
Auto merge of #12354 - GuillaumeGomez:mixed_attributes_style, r=llogiq
Add new `mixed_attributes_style` lint

Add a new lint to detect cases where both inner and outer attributes are used on a same item.

r? `@llogiq`

----

changelog: Add new [`mixed_attributes_style`] lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/attrs.rs55
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--tests/ui/empty_docs.rs2
-rw-r--r--tests/ui/empty_docs.stderr18
-rw-r--r--tests/ui/mixed_attributes_style.rs39
-rw-r--r--tests/ui/mixed_attributes_style.stderr30
7 files changed, 137 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f59f3e8e089..fb750706403 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5423,6 +5423,7 @@ Released 2018-09-13
 [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
 [`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
 [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
+[`mixed_attributes_style`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style
 [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 [`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index f131bb40043..5d74c3e6203 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -485,6 +485,33 @@ declare_clippy_lint! {
     "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks that an item has only one kind of attributes.
+    ///
+    /// ### Why is this bad?
+    /// Having both kinds of attributes makes it more complicated to read code.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// #[cfg(linux)]
+    /// pub fn foo() {
+    ///     #![cfg(windows)]
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// #[cfg(linux)]
+    /// #[cfg(windows)]
+    /// pub fn foo() {
+    /// }
+    /// ```
+    #[clippy::version = "1.78.0"]
+    pub MIXED_ATTRIBUTES_STYLE,
+    suspicious,
+    "item has both inner and outer attributes"
+}
+
 declare_lint_pass!(Attributes => [
     ALLOW_ATTRIBUTES_WITHOUT_REASON,
     INLINE_ALWAYS,
@@ -849,11 +876,13 @@ impl_lint_pass!(EarlyAttributes => [
     MAYBE_MISUSED_CFG,
     DEPRECATED_CLIPPY_CFG_ATTR,
     UNNECESSARY_CLIPPY_CFG,
+    MIXED_ATTRIBUTES_STYLE,
 ]);
 
 impl EarlyLintPass for EarlyAttributes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
         check_empty_line_after_outer_attr(cx, item);
+        check_mixed_attributes(cx, item);
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
@@ -867,6 +896,32 @@ impl EarlyLintPass for EarlyAttributes {
     extract_msrv_attr!(EarlyContext);
 }
 
+fn check_mixed_attributes(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
+    let mut has_outer = false;
+    let mut has_inner = false;
+
+    for attr in &item.attrs {
+        if attr.span.from_expansion() {
+            continue;
+        }
+        match attr.style {
+            AttrStyle::Inner => has_inner = true,
+            AttrStyle::Outer => has_outer = true,
+        }
+    }
+    if !has_outer || !has_inner {
+        return;
+    }
+    let mut attrs_iter = item.attrs.iter().filter(|attr| !attr.span.from_expansion());
+    let span = attrs_iter.next().unwrap().span;
+    span_lint(
+        cx,
+        MIXED_ATTRIBUTES_STYLE,
+        span.with_hi(attrs_iter.last().unwrap().span.hi()),
+        "item has both inner and outer attributes",
+    );
+}
+
 /// Check for empty lines after outer attributes.
 ///
 /// Attributes and documentation comments are both considered outer attributes
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index fb3ae2457e3..a453386154d 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -58,6 +58,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::attrs::INLINE_ALWAYS_INFO,
     crate::attrs::MAYBE_MISUSED_CFG_INFO,
     crate::attrs::MISMATCHED_TARGET_OS_INFO,
+    crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
     crate::attrs::NON_MINIMAL_CFG_INFO,
     crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
     crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO,
diff --git a/tests/ui/empty_docs.rs b/tests/ui/empty_docs.rs
index c47a603e71b..272fab7d5ca 100644
--- a/tests/ui/empty_docs.rs
+++ b/tests/ui/empty_docs.rs
@@ -1,5 +1,7 @@
 #![allow(unused)]
 #![warn(clippy::empty_docs)]
+#![allow(clippy::mixed_attributes_style)]
+
 mod outer {
     //!
 
diff --git a/tests/ui/empty_docs.stderr b/tests/ui/empty_docs.stderr
index 3f1d071fb13..f12aead6aa7 100644
--- a/tests/ui/empty_docs.stderr
+++ b/tests/ui/empty_docs.stderr
@@ -1,5 +1,5 @@
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:4:5
+  --> tests/ui/empty_docs.rs:6:5
    |
 LL |     //!
    |     ^^^
@@ -9,7 +9,7 @@ LL |     //!
    = help: to override `-D warnings` add `#[allow(clippy::empty_docs)]`
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:12:5
+  --> tests/ui/empty_docs.rs:14:5
    |
 LL |     ///
    |     ^^^
@@ -17,7 +17,7 @@ LL |     ///
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:14:9
+  --> tests/ui/empty_docs.rs:16:9
    |
 LL |         ///
    |         ^^^
@@ -25,7 +25,7 @@ LL |         ///
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:25:5
+  --> tests/ui/empty_docs.rs:27:5
    |
 LL |     #[doc = ""]
    |     ^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL |     #[doc = ""]
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:28:5
+  --> tests/ui/empty_docs.rs:30:5
    |
 LL | /     #[doc = ""]
 LL | |     #[doc = ""]
@@ -42,7 +42,7 @@ LL | |     #[doc = ""]
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:35:5
+  --> tests/ui/empty_docs.rs:37:5
    |
 LL |     ///
    |     ^^^
@@ -50,7 +50,7 @@ LL |     ///
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:48:13
+  --> tests/ui/empty_docs.rs:50:13
    |
 LL |             /*! */
    |             ^^^^^^
@@ -58,7 +58,7 @@ LL |             /*! */
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:56:13
+  --> tests/ui/empty_docs.rs:58:13
    |
 LL |             ///
    |             ^^^
@@ -66,7 +66,7 @@ LL |             ///
    = help: consider removing or filling it
 
 error: empty doc comment
-  --> tests/ui/empty_docs.rs:64:9
+  --> tests/ui/empty_docs.rs:66:9
    |
 LL |         ///
    |         ^^^
diff --git a/tests/ui/mixed_attributes_style.rs b/tests/ui/mixed_attributes_style.rs
new file mode 100644
index 00000000000..ad93e3019fa
--- /dev/null
+++ b/tests/ui/mixed_attributes_style.rs
@@ -0,0 +1,39 @@
+#![warn(clippy::mixed_attributes_style)]
+
+#[allow(unused)] //~ ERROR: item has both inner and outer attributes
+fn foo1() {
+    #![allow(unused)]
+}
+
+#[allow(unused)]
+#[allow(unused)]
+fn foo2() {}
+
+fn foo3() {
+    #![allow(unused)]
+    #![allow(unused)]
+}
+
+/// linux
+//~^ ERROR: item has both inner and outer attributes
+fn foo4() {
+    //! windows
+}
+
+/// linux
+/// windows
+fn foo5() {}
+
+fn foo6() {
+    //! linux
+    //! windows
+}
+
+#[allow(unused)] //~ ERROR: item has both inner and outer attributes
+mod bar {
+    #![allow(unused)]
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/tests/ui/mixed_attributes_style.stderr b/tests/ui/mixed_attributes_style.stderr
new file mode 100644
index 00000000000..d1d5cd3f47f
--- /dev/null
+++ b/tests/ui/mixed_attributes_style.stderr
@@ -0,0 +1,30 @@
+error: item has both inner and outer attributes
+  --> tests/ui/mixed_attributes_style.rs:3:1
+   |
+LL | / #[allow(unused)]
+LL | | fn foo1() {
+LL | |     #![allow(unused)]
+   | |_____________________^
+   |
+   = note: `-D clippy::mixed-attributes-style` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]`
+
+error: item has both inner and outer attributes
+  --> tests/ui/mixed_attributes_style.rs:17:1
+   |
+LL | / /// linux
+LL | |
+LL | | fn foo4() {
+LL | |     //! windows
+   | |_______________^
+
+error: item has both inner and outer attributes
+  --> tests/ui/mixed_attributes_style.rs:32:1
+   |
+LL | / #[allow(unused)]
+LL | | mod bar {
+LL | |     #![allow(unused)]
+   | |_____________________^
+
+error: aborting due to 3 previous errors
+