Skip to content

Commit 291385b

Browse files
committed
Auto merge of rust-lang#5609 - phansch:empty-line-after-outer-attr-fix, r=flip1995
Make empty_line_after_outer_attr an early lint Fixes rust-lang#5567 Unfortunately I couldn't find a way to reproduce the issue without syn/quote. Considering that most real-world macros use syn and/or quote, I think it's okay to pull them in anyway. changelog: Fix false positive in [`empty_line_after_outer_attr`]
2 parents a00025a + fd86b31 commit 291385b

File tree

6 files changed

+107
-40
lines changed

6 files changed

+107
-40
lines changed

clippy_lints/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ semver = "0.9.0"
3232
# NOTE: cargo requires serde feat in its url dep
3333
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
3434
url = { version = "2.1.0", features = ["serde"] }
35+
quote = "1"
36+
syn = { version = "1", features = ["full"] }
3537

3638
[features]
3739
deny-warnings = []

clippy_lints/src/attrs.rs

+43-32
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [
248248
INLINE_ALWAYS,
249249
DEPRECATED_SEMVER,
250250
USELESS_ATTRIBUTE,
251-
EMPTY_LINE_AFTER_OUTER_ATTR,
252251
UNKNOWN_CLIPPY_LINTS,
253252
]);
254253

@@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib
480479
}
481480

482481
for attr in attrs {
483-
let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
484-
attr
485-
} else {
486-
continue;
487-
};
488-
489-
if attr.style == AttrStyle::Outer {
490-
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
491-
return;
492-
}
493-
494-
let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt());
495-
let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt());
496-
497-
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
498-
let lines = snippet.split('\n').collect::<Vec<_>>();
499-
let lines = without_block_comments(lines);
500-
501-
if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
502-
span_lint(
503-
cx,
504-
EMPTY_LINE_AFTER_OUTER_ATTR,
505-
begin_of_attr_to_item,
506-
"Found an empty line after an outer attribute. \
507-
Perhaps you forgot to add a `!` to make it an inner attribute?",
508-
);
509-
}
510-
}
511-
}
512-
513482
if let Some(values) = attr.meta_item_list() {
514483
if values.len() != 1 || !attr.check_name(sym!(inline)) {
515484
continue;
@@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
551520
}
552521
}
553522

554-
declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
523+
declare_lint_pass!(EarlyAttributes => [
524+
DEPRECATED_CFG_ATTR,
525+
MISMATCHED_TARGET_OS,
526+
EMPTY_LINE_AFTER_OUTER_ATTR,
527+
]);
555528

556529
impl EarlyLintPass for EarlyAttributes {
530+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
531+
check_empty_line_after_outer_attr(cx, item);
532+
}
533+
557534
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
558535
check_deprecated_cfg_attr(cx, attr);
559536
check_mismatched_target_os(cx, attr);
560537
}
561538
}
562539

540+
fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
541+
for attr in &item.attrs {
542+
let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
543+
attr
544+
} else {
545+
return;
546+
};
547+
548+
if attr.style == AttrStyle::Outer {
549+
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
550+
return;
551+
}
552+
553+
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt());
554+
let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt());
555+
556+
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
557+
let lines = snippet.split('\n').collect::<Vec<_>>();
558+
let lines = without_block_comments(lines);
559+
560+
if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
561+
span_lint(
562+
cx,
563+
EMPTY_LINE_AFTER_OUTER_ATTR,
564+
begin_of_attr_to_item,
565+
"Found an empty line after an outer attribute. \
566+
Perhaps you forgot to add a `!` to make it an inner attribute?",
567+
);
568+
}
569+
}
570+
}
571+
}
572+
}
573+
563574
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
564575
if_chain! {
565576
// check cfg_attr

tests/compile-test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf {
3838
// as what we manually pass to `cargo` invocation
3939
fn third_party_crates() -> String {
4040
use std::collections::HashMap;
41-
static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"];
41+
static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
4242
let dep_dir = cargo::TARGET_LIB.join("deps");
4343
let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
4444
for entry in fs::read_dir(dep_dir).unwrap() {

tests/ui/auxiliary/proc_macro_attr.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// no-prefer-dynamic
2+
3+
#![crate_type = "proc-macro"]
4+
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
5+
#![allow(clippy::useless_conversion)]
6+
7+
extern crate proc_macro;
8+
extern crate quote;
9+
extern crate syn;
10+
11+
use proc_macro::TokenStream;
12+
use quote::{quote, quote_spanned};
13+
use syn::parse_macro_input;
14+
use syn::{parse_quote, ItemTrait, TraitItem};
15+
16+
#[proc_macro_attribute]
17+
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
18+
let mut item = parse_macro_input!(input as ItemTrait);
19+
for inner in &mut item.items {
20+
if let TraitItem::Method(method) = inner {
21+
let sig = &method.sig;
22+
let block = &mut method.default;
23+
if let Some(block) = block {
24+
let brace = block.brace_token;
25+
26+
let my_block = quote_spanned!( brace.span => {
27+
// Should not trigger `empty_line_after_outer_attr`
28+
#[crate_type = "lib"]
29+
#sig #block
30+
Vec::new()
31+
});
32+
*block = parse_quote!(#my_block);
33+
}
34+
}
35+
}
36+
TokenStream::from(quote!(#item))
37+
}

tests/ui/empty_line_after_outer_attribute.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
// aux-build:proc_macro_attr.rs
12
#![warn(clippy::empty_line_after_outer_attr)]
23
#![allow(clippy::assertions_on_constants)]
34
#![feature(custom_inner_attributes)]
45
#![rustfmt::skip]
56

7+
#[macro_use]
8+
extern crate proc_macro_attr;
9+
610
// This should produce a warning
711
#[crate_type = "lib"]
812

@@ -93,4 +97,17 @@ pub struct S;
9397
/* test */
9498
pub struct T;
9599

96-
fn main() { }
100+
// This should not produce a warning
101+
// See https://github.com/rust-lang/rust-clippy/issues/5567
102+
#[fake_async_trait]
103+
pub trait Bazz {
104+
fn foo() -> Vec<u8> {
105+
let _i = "";
106+
107+
108+
109+
vec![]
110+
}
111+
}
112+
113+
fn main() {}

tests/ui/empty_line_after_outer_attribute.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
2-
--> $DIR/empty_line_after_outer_attribute.rs:7:1
2+
--> $DIR/empty_line_after_outer_attribute.rs:11:1
33
|
44
LL | / #[crate_type = "lib"]
55
LL | |
@@ -10,15 +10,15 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
1010
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
1111

1212
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
13-
--> $DIR/empty_line_after_outer_attribute.rs:19:1
13+
--> $DIR/empty_line_after_outer_attribute.rs:23:1
1414
|
1515
LL | / #[crate_type = "lib"]
1616
LL | |
1717
LL | | fn with_one_newline() { assert!(true) }
1818
| |_
1919

2020
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
21-
--> $DIR/empty_line_after_outer_attribute.rs:24:1
21+
--> $DIR/empty_line_after_outer_attribute.rs:28:1
2222
|
2323
LL | / #[crate_type = "lib"]
2424
LL | |
@@ -27,23 +27,23 @@ LL | | fn with_two_newlines() { assert!(true) }
2727
| |_
2828

2929
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
30-
--> $DIR/empty_line_after_outer_attribute.rs:31:1
30+
--> $DIR/empty_line_after_outer_attribute.rs:35:1
3131
|
3232
LL | / #[crate_type = "lib"]
3333
LL | |
3434
LL | | enum Baz {
3535
| |_
3636

3737
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
38-
--> $DIR/empty_line_after_outer_attribute.rs:39:1
38+
--> $DIR/empty_line_after_outer_attribute.rs:43:1
3939
|
4040
LL | / #[crate_type = "lib"]
4141
LL | |
4242
LL | | struct Foo {
4343
| |_
4444

4545
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
46-
--> $DIR/empty_line_after_outer_attribute.rs:47:1
46+
--> $DIR/empty_line_after_outer_attribute.rs:51:1
4747
|
4848
LL | / #[crate_type = "lib"]
4949
LL | |

0 commit comments

Comments
 (0)