mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 21:23:56 +00:00
Make empty_line_after_outer_attr an early lint
This commit is contained in:
parent
578692d9b0
commit
67167be167
6 changed files with 109 additions and 40 deletions
|
@ -33,5 +33,9 @@ semver = "0.9.0"
|
||||||
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
|
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
|
||||||
url = { version = "2.1.0", features = ["serde"] }
|
url = { version = "2.1.0", features = ["serde"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
quote = "*"
|
||||||
|
syn = { version = "*", features = ["full"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
deny-warnings = []
|
deny-warnings = []
|
||||||
|
|
|
@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [
|
||||||
INLINE_ALWAYS,
|
INLINE_ALWAYS,
|
||||||
DEPRECATED_SEMVER,
|
DEPRECATED_SEMVER,
|
||||||
USELESS_ATTRIBUTE,
|
USELESS_ATTRIBUTE,
|
||||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
|
||||||
UNKNOWN_CLIPPY_LINTS,
|
UNKNOWN_CLIPPY_LINTS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr in attrs {
|
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 let Some(values) = attr.meta_item_list() {
|
||||||
if values.len() != 1 || !attr.check_name(sym!(inline)) {
|
if values.len() != 1 || !attr.check_name(sym!(inline)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -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 {
|
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) {
|
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||||
check_deprecated_cfg_attr(cx, attr);
|
check_deprecated_cfg_attr(cx, attr);
|
||||||
check_mismatched_target_os(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) {
|
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
// check cfg_attr
|
// check cfg_attr
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf {
|
||||||
// as what we manually pass to `cargo` invocation
|
// as what we manually pass to `cargo` invocation
|
||||||
fn third_party_crates() -> String {
|
fn third_party_crates() -> String {
|
||||||
use std::collections::HashMap;
|
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 dep_dir = cargo::TARGET_LIB.join("deps");
|
||||||
let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
|
let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
|
||||||
for entry in fs::read_dir(dep_dir).unwrap() {
|
for entry in fs::read_dir(dep_dir).unwrap() {
|
||||||
|
|
37
tests/ui/auxiliary/proc_macro_attr.rs
Normal file
37
tests/ui/auxiliary/proc_macro_attr.rs
Normal file
|
@ -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))
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
|
// aux-build:proc_macro_attr.rs
|
||||||
#![warn(clippy::empty_line_after_outer_attr)]
|
#![warn(clippy::empty_line_after_outer_attr)]
|
||||||
#![allow(clippy::assertions_on_constants)]
|
#![allow(clippy::assertions_on_constants)]
|
||||||
#![feature(custom_inner_attributes)]
|
#![feature(custom_inner_attributes)]
|
||||||
#![rustfmt::skip]
|
#![rustfmt::skip]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate proc_macro_attr;
|
||||||
|
|
||||||
// This should produce a warning
|
// This should produce a warning
|
||||||
#[crate_type = "lib"]
|
#[crate_type = "lib"]
|
||||||
|
|
||||||
|
@ -93,4 +97,17 @@ pub struct S;
|
||||||
/* test */
|
/* test */
|
||||||
pub struct T;
|
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() {}
|
||||||
|
|
|
@ -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?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
|
||||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
= 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?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -18,7 +18,7 @@ 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?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -27,7 +27,7 @@ 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?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -35,7 +35,7 @@ LL | | enum Baz {
|
||||||
| |_
|
| |_
|
||||||
|
|
||||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -43,7 +43,7 @@ LL | | struct Foo {
|
||||||
| |_
|
| |_
|
||||||
|
|
||||||
error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
|
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 | / #[crate_type = "lib"]
|
||||||
LL | |
|
LL | |
|
||||||
|
|
Loading…
Reference in a new issue