about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml7
-rw-r--r--clippy_lints/src/doc/empty_line_after.rs20
-rw-r--r--tests/ui/allow_attributes.fixed7
-rw-r--r--tests/ui/allow_attributes.rs7
-rw-r--r--tests/ui/empty_line_after/outer_attribute.1.fixed5
-rw-r--r--tests/ui/empty_line_after/outer_attribute.2.fixed5
-rw-r--r--tests/ui/empty_line_after/outer_attribute.rs7
-rw-r--r--tests/ui/empty_line_after/outer_attribute.stderr15
8 files changed, 70 insertions, 3 deletions
diff --git a/Cargo.toml b/Cargo.toml
index cf810798d8c..ddc27179ef2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -67,3 +67,10 @@ harness = false
 [[test]]
 name = "dogfood"
 harness = false
+
+# quine-mc_cluskey makes up a significant part of the runtime in dogfood
+# due to the number of conditions in the clippy_lints crate
+# and enabling optimizations for that specific dependency helps a bit
+# without increasing total build times.
+[profile.dev.package.quine-mc_cluskey]
+opt-level = 3
diff --git a/clippy_lints/src/doc/empty_line_after.rs b/clippy_lints/src/doc/empty_line_after.rs
index 125b9077061..de7a2c2433f 100644
--- a/clippy_lints/src/doc/empty_line_after.rs
+++ b/clippy_lints/src/doc/empty_line_after.rs
@@ -8,7 +8,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle};
 use rustc_hir::{ItemKind, Node};
 use rustc_lexer::TokenKind;
 use rustc_lint::LateContext;
-use rustc_span::{ExpnKind, InnerSpan, Span, SpanData};
+use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData};
 
 use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
 
@@ -144,6 +144,19 @@ impl<'a> Gap<'a> {
             prev_chunk,
         })
     }
+
+    fn contiguous_empty_lines(&self) -> impl Iterator<Item = Span> + '_ {
+        self.empty_lines
+            // The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1".
+            .chunk_by(|a, b| a.hi() + BytePos(1) == b.lo())
+            .map(|chunk| {
+                let first = chunk.first().expect("at least one empty line");
+                let last = chunk.last().expect("at least one empty line");
+                // The BytePos subtraction here is safe, as before an empty line, there must be at least one
+                // attribute/comment. The span needs to start at the end of the previous line.
+                first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi())
+            })
+    }
 }
 
 /// If the node the attributes/docs apply to is the first in the module/crate suggest converting
@@ -192,6 +205,7 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
         return false;
     };
     let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied());
+    let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines);
     let mut has_comment = false;
     let mut has_attr = false;
     for gap in gaps {
@@ -227,7 +241,9 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
 
             diag.multipart_suggestion_with_style(
                 format!("if the empty {lines} {are} unintentional remove {them}"),
-                empty_lines().map(|empty_line| (empty_line, String::new())).collect(),
+                contiguous_empty_lines()
+                    .map(|empty_lines| (empty_lines, String::new()))
+                    .collect(),
                 Applicability::MaybeIncorrect,
                 SuggestionStyle::HideCodeAlways,
             );
diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed
index 49ee3ee17c7..058dbb77a32 100644
--- a/tests/ui/allow_attributes.fixed
+++ b/tests/ui/allow_attributes.fixed
@@ -58,3 +58,10 @@ fn msrv_1_80() {
     #[allow(unused)]
     let x = 1;
 }
+
+#[deny(clippy::allow_attributes)]
+fn deny_allow_attributes() -> Option<u8> {
+    let allow = None;
+    allow?;
+    Some(42)
+}
diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs
index 854acf8348d..6d94ce50e4c 100644
--- a/tests/ui/allow_attributes.rs
+++ b/tests/ui/allow_attributes.rs
@@ -58,3 +58,10 @@ fn msrv_1_80() {
     #[allow(unused)]
     let x = 1;
 }
+
+#[deny(clippy::allow_attributes)]
+fn deny_allow_attributes() -> Option<u8> {
+    let allow = None;
+    allow?;
+    Some(42)
+}
diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed
index cd7ea24b6be..36d80a2c95b 100644
--- a/tests/ui/empty_line_after/outer_attribute.1.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.1.fixed
@@ -51,6 +51,11 @@ mod foo {}
 // Still lint cases where the empty line does not immediately follow the attribute
 fn comment_before_empty_line() {}
 
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+// This comment is isolated
+pub fn isolated_comment() {}
+
 #[doc = "
 Returns the escaped value of the textual representation of
 
diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed
index 1b044d2fcde..0e8e4129e85 100644
--- a/tests/ui/empty_line_after/outer_attribute.2.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.2.fixed
@@ -54,6 +54,11 @@ mod foo {}
 // Still lint cases where the empty line does not immediately follow the attribute
 fn comment_before_empty_line() {}
 
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+// This comment is isolated
+pub fn isolated_comment() {}
+
 #[doc = "
 Returns the escaped value of the textual representation of
 
diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs
index 81e1a7ab8ed..1295088ac00 100644
--- a/tests/ui/empty_line_after/outer_attribute.rs
+++ b/tests/ui/empty_line_after/outer_attribute.rs
@@ -60,6 +60,13 @@ mod foo {}
 
 fn comment_before_empty_line() {}
 
+//~v empty_line_after_outer_attr
+#[allow(unused)]
+
+// This comment is isolated
+
+pub fn isolated_comment() {}
+
 #[doc = "
 Returns the escaped value of the textual representation of
 
diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr
index b73ebb4f662..958b40424a9 100644
--- a/tests/ui/empty_line_after/outer_attribute.stderr
+++ b/tests/ui/empty_line_after/outer_attribute.stderr
@@ -99,5 +99,18 @@ LL |   fn comment_before_empty_line() {}
    |
    = help: if the empty line is unintentional remove it
 
-error: aborting due to 8 previous errors
+error: empty lines after outer attribute
+  --> tests/ui/empty_line_after/outer_attribute.rs:64:1
+   |
+LL | / #[allow(unused)]
+LL | |
+LL | | // This comment is isolated
+LL | |
+   | |_
+LL |   pub fn isolated_comment() {}
+   |   ------------------------- the attribute applies to this function
+   |
+   = help: if the empty lines are unintentional remove them
+
+error: aborting due to 9 previous errors