Fix suggestion-cases-error of `empty_line_after_outer_attr`
diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs
index 0c5f8bb..3bd7485 100644
--- a/clippy_lints/src/empty_line_after.rs
+++ b/clippy_lints/src/empty_line_after.rs
@@ -10,7 +10,7 @@
use rustc_lexer::TokenKind;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::impl_lint_pass;
-use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw};
+use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym};
declare_clippy_lint! {
/// ### What it does
@@ -129,10 +129,55 @@
kind: StopKind,
first: usize,
last: usize,
+ name: Option<Symbol>,
}
impl Stop {
- fn convert_to_inner(&self) -> (Span, String) {
+ fn is_outer_attr_only(&self) -> bool {
+ let Some(name) = self.name else {
+ return false;
+ };
+ // Check if the attribute only has effect when as an outer attribute
+ // The below attributes are collected from the builtin attributes of The Rust Reference
+ // https://doc.rust-lang.org/reference/attributes.html#r-attributes.builtin
+ // And the comments below are from compiler errors and warnings
+ matches!(
+ name,
+ // Cannot be used at crate level
+ sym::repr | sym::test | sym::derive | sym::automatically_derived | sym::path | sym::global_allocator |
+ // Only has an effect on macro definitions
+ sym::macro_export |
+ // Only be applied to trait definitions
+ sym::on_unimplemented |
+ // Only be placed on trait implementations
+ sym::do_not_recommend |
+ // Only has an effect on items
+ sym::ignore | sym::should_panic | sym::proc_macro | sym::proc_macro_derive | sym::proc_macro_attribute |
+ // Has no effect when applied to a module
+ sym::must_use |
+ // Should be applied to a foreign function or static
+ sym::link_name | sym::link_ordinal | sym::link_section |
+ // Should be applied to an `extern crate` item
+ sym::no_link |
+ // Should be applied to a free function, impl method or static
+ sym::export_name | sym::no_mangle |
+ // Should be applied to a `static` variable
+ sym::used |
+ // Should be applied to function or closure
+ sym::inline |
+ // Should be applied to a function definition
+ sym::cold | sym::target_feature | sym::track_caller | sym::instruction_set |
+ // Should be applied to a struct or enum
+ sym::non_exhaustive |
+ // Note: No any warning when it as an inner attribute, but it has no effect
+ sym::panic_handler
+ )
+ }
+
+ fn convert_to_inner(&self) -> Option<(Span, String)> {
+ if self.is_outer_attr_only() {
+ return None;
+ }
let inner = match self.kind {
// #![...]
StopKind::Attr => InnerSpan::new(1, 1),
@@ -140,7 +185,7 @@
// ^ ^
StopKind::Doc(_) => InnerSpan::new(2, 3),
};
- (self.span.from_inner(inner), "!".into())
+ Some((self.span.from_inner(inner), "!".into()))
}
fn comment_out(&self, cx: &EarlyContext<'_>, suggestions: &mut Vec<(Span, String)>) {
@@ -177,6 +222,7 @@
},
first: file.lookup_line(file.relative_position(lo))?,
last: file.lookup_line(file.relative_position(hi))?,
+ name: attr.name(),
})
}
}
@@ -356,6 +402,12 @@
if let Some(parent) = self.items.iter().rev().nth(1)
&& (parent.kind == "module" || parent.kind == "crate")
&& parent.mod_items == Some(id)
+ && let suggestions = gaps
+ .iter()
+ .flat_map(|gap| gap.prev_chunk)
+ .filter_map(Stop::convert_to_inner)
+ .collect::<Vec<_>>()
+ && !suggestions.is_empty()
{
let desc = if parent.kind == "module" {
"parent module"
@@ -367,10 +419,7 @@
StopKind::Attr => format!("if the attribute should apply to the {desc} use an inner attribute"),
StopKind::Doc(_) => format!("if the comment should document the {desc} use an inner doc comment"),
},
- gaps.iter()
- .flat_map(|gap| gap.prev_chunk)
- .map(Stop::convert_to_inner)
- .collect(),
+ suggestions,
Applicability::MaybeIncorrect,
);
}
@@ -425,6 +474,7 @@
first: line.line,
// last doesn't need to be accurate here, we don't compare it with anything
last: line.line,
+ name: None,
});
}
diff --git a/tests/ui/empty_line_after/outer_attribute.1.fixed b/tests/ui/empty_line_after/outer_attribute.1.fixed
index 36d80a2..e36e3c2 100644
--- a/tests/ui/empty_line_after/outer_attribute.1.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.1.fixed
@@ -105,4 +105,13 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.2.fixed b/tests/ui/empty_line_after/outer_attribute.2.fixed
index 0e8e412..b0908fc 100644
--- a/tests/ui/empty_line_after/outer_attribute.2.fixed
+++ b/tests/ui/empty_line_after/outer_attribute.2.fixed
@@ -108,4 +108,13 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.rs b/tests/ui/empty_line_after/outer_attribute.rs
index 1295088..4ae113c 100644
--- a/tests/ui/empty_line_after/outer_attribute.rs
+++ b/tests/ui/empty_line_after/outer_attribute.rs
@@ -116,4 +116,14 @@
")]
pub struct Args;
+mod issue_14980 {
+ //~v empty_line_after_outer_attr
+ #[repr(align(536870912))]
+
+ enum Aligned {
+ Zero = 0,
+ One = 1,
+ }
+}
+
fn main() {}
diff --git a/tests/ui/empty_line_after/outer_attribute.stderr b/tests/ui/empty_line_after/outer_attribute.stderr
index 519ba6e..331bc7c 100644
--- a/tests/ui/empty_line_after/outer_attribute.stderr
+++ b/tests/ui/empty_line_after/outer_attribute.stderr
@@ -111,5 +111,16 @@
|
= help: if the empty lines are unintentional, remove them
-error: aborting due to 9 previous errors
+error: empty line after outer attribute
+ --> tests/ui/empty_line_after/outer_attribute.rs:121:5
+ |
+LL | / #[repr(align(536870912))]
+LL | |
+ | |_^
+LL | enum Aligned {
+ | ------------ the attribute applies to this enum
+ |
+ = help: if the empty line is unintentional, remove it
+
+error: aborting due to 10 previous errors