Skip to content

Commit

Permalink
Make empty_line_after_outer_attr an early lint
Browse files Browse the repository at this point in the history
  • Loading branch information
phansch committed May 26, 2020
1 parent 578692d commit 67167be
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 40 deletions.
4 changes: 4 additions & 0 deletions clippy_lints/Cargo.toml
Expand Up @@ -33,5 +33,9 @@ semver = "0.9.0"
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
url = { version = "2.1.0", features = ["serde"] }

[dev-dependencies]
quote = "*"
syn = { version = "*", features = ["full"] }

[features]
deny-warnings = []
75 changes: 43 additions & 32 deletions clippy_lints/src/attrs.rs
Expand Up @@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [
INLINE_ALWAYS,
DEPRECATED_SEMVER,
USELESS_ATTRIBUTE,
EMPTY_LINE_AFTER_OUTER_ATTR,
UNKNOWN_CLIPPY_LINTS,
]);

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

for attr in attrs {
let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
attr
} else {
continue;
};

if attr.style == AttrStyle::Outer {
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
return;
}

let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt());
let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt());

if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
let lines = snippet.split('\n').collect::<Vec<_>>();
let lines = without_block_comments(lines);

if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
span_lint(
cx,
EMPTY_LINE_AFTER_OUTER_ATTR,
begin_of_attr_to_item,
"Found an empty line after an outer attribute. \
Perhaps you forgot to add a `!` to make it an inner attribute?",
);
}
}
}

if let Some(values) = attr.meta_item_list() {
if values.len() != 1 || !attr.check_name(sym!(inline)) {
continue;
Expand Down Expand Up @@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
}
}

declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
declare_lint_pass!(EarlyAttributes => [
DEPRECATED_CFG_ATTR,
MISMATCHED_TARGET_OS,
EMPTY_LINE_AFTER_OUTER_ATTR,
]);

impl EarlyLintPass for EarlyAttributes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
check_empty_line_after_outer_attr(cx, item);
}

fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
check_deprecated_cfg_attr(cx, attr);
check_mismatched_target_os(cx, attr);
}
}

fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
for attr in &item.attrs {
let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
attr
} else {
return;
};

if attr.style == AttrStyle::Outer {
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
return;
}

let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt());
let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt());

if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
let lines = snippet.split('\n').collect::<Vec<_>>();
let lines = without_block_comments(lines);

if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
span_lint(
cx,
EMPTY_LINE_AFTER_OUTER_ATTR,
begin_of_attr_to_item,
"Found an empty line after an outer attribute. \
Perhaps you forgot to add a `!` to make it an inner attribute?",
);
}
}
}
}
}

fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
if_chain! {
// check cfg_attr
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-test.rs
Expand Up @@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf {
// as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
use std::collections::HashMap;
static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"];
static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
let dep_dir = cargo::TARGET_LIB.join("deps");
let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
for entry in fs::read_dir(dep_dir).unwrap() {
Expand Down
37 changes: 37 additions & 0 deletions tests/ui/auxiliary/proc_macro_attr.rs
@@ -0,0 +1,37 @@
// no-prefer-dynamic

#![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
#![allow(clippy::useless_conversion)]

extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::parse_macro_input;
use syn::{parse_quote, ItemTrait, TraitItem};

#[proc_macro_attribute]
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut item = parse_macro_input!(input as ItemTrait);
for inner in &mut item.items {
if let TraitItem::Method(method) = inner {
let sig = &method.sig;
let block = &mut method.default;
if let Some(block) = block {
let brace = block.brace_token;

let my_block = quote_spanned!( brace.span => {
// Should not trigger `empty_line_after_outer_attr`
#[crate_type = "lib"]
#sig #block
Vec::new()
});
*block = parse_quote!(#my_block);
}
}
}
TokenStream::from(quote!(#item))
}
19 changes: 18 additions & 1 deletion tests/ui/empty_line_after_outer_attribute.rs
@@ -1,8 +1,12 @@
// aux-build:proc_macro_attr.rs
#![warn(clippy::empty_line_after_outer_attr)]
#![allow(clippy::assertions_on_constants)]
#![feature(custom_inner_attributes)]
#![rustfmt::skip]

#[macro_use]
extern crate proc_macro_attr;

// This should produce a warning
#[crate_type = "lib"]

Expand Down Expand Up @@ -93,4 +97,17 @@ pub struct S;
/* test */
pub struct T;

fn main() { }
// This should not produce a warning
// See https://github.com/rust-lang/rust-clippy/issues/5567
#[fake_async_trait]
pub trait Bazz {
fn foo() -> Vec<u8> {
let _i = "";



vec![]
}
}

fn main() {}
12 changes: 6 additions & 6 deletions tests/ui/empty_line_after_outer_attribute.stderr
@@ -1,5 +1,5 @@
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
--> $DIR/empty_line_after_outer_attribute.rs:7:1
--> $DIR/empty_line_after_outer_attribute.rs:11:1
|
LL | / #[crate_type = "lib"]
LL | |
Expand All @@ -10,15 +10,15 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`

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

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

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

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

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

0 comments on commit 67167be

Please sign in to comment.