Skip to content

Fix suggestion-cases-error of empty_line_after_outer_attr #15078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 57 additions & 7 deletions clippy_lints/src/empty_line_after.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle};
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
Expand Down Expand Up @@ -129,18 +129,63 @@ struct Stop {
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),
// /// or /**
// ^ ^
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)>) {
Expand Down Expand Up @@ -177,6 +222,7 @@ impl Stop {
},
first: file.lookup_line(file.relative_position(lo))?,
last: file.lookup_line(file.relative_position(hi))?,
name: attr.name(),
})
}
}
Expand Down Expand Up @@ -356,6 +402,12 @@ impl EmptyLineAfter {
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"
Expand All @@ -367,10 +419,7 @@ impl EmptyLineAfter {
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,
);
}
Expand Down Expand Up @@ -425,6 +474,7 @@ impl EmptyLineAfter {
first: line.line,
// last doesn't need to be accurate here, we don't compare it with anything
last: line.line,
name: None,
});
}

Expand Down
9 changes: 9 additions & 0 deletions tests/ui/empty_line_after/outer_attribute.1.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,13 @@ second line
")]
pub struct Args;

mod issue_14980 {
//~v empty_line_after_outer_attr
#[repr(align(536870912))]
enum Aligned {
Zero = 0,
One = 1,
}
}

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/empty_line_after/outer_attribute.2.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,13 @@ second line
")]
pub struct Args;

mod issue_14980 {
//~v empty_line_after_outer_attr
#[repr(align(536870912))]
enum Aligned {
Zero = 0,
One = 1,
}
}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/empty_line_after/outer_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,14 @@ second line
")]
pub struct Args;

mod issue_14980 {
//~v empty_line_after_outer_attr
#[repr(align(536870912))]

enum Aligned {
Zero = 0,
One = 1,
}
}

fn main() {}
13 changes: 12 additions & 1 deletion tests/ui/empty_line_after/outer_attribute.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,16 @@ LL | pub fn isolated_comment() {}
|
= 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