diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index da7fff2ed93..a96193bacf1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -5,7 +5,7 @@ use rustc::lint::*; use rustc::hir::*; use rustc::ty::{self, TyCtxt}; use semver::Version; -use syntax::ast::{Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; +use syntax::ast::{Attribute, AttrStyle, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; use syntax::codemap::Span; use utils::{in_macro, match_def_path, opt_def_id, paths, snippet_opt, span_lint, span_lint_and_then}; @@ -78,12 +78,44 @@ declare_lint! { "use of `#[deprecated(since = \"x\")]` where x is not semver" } +/// **What it does:** Checks for empty lines after outer attributes +/// +/// **Why is this bad?** +/// Most likely the attribute was meant to be an inner attribute using a '!'. +/// If it was meant to be an outer attribute, then the following item +/// should not be separated by empty lines. +/// +/// **Known problems:** None +/// +/// **Example:** +/// ```rust +/// // Bad +/// #[inline(always)] +/// +/// fn not_quite_good_code(..) { ... } +/// +/// // Good (as inner attribute) +/// #![inline(always)] +/// +/// fn this_is_fine_too(..) { ... } +/// +/// // Good (as outer attribute) +/// #[inline(always)] +/// fn this_is_fine(..) { ... } +/// +/// ``` +declare_lint! { + pub EMPTY_LINE_AFTER_OUTER_ATTR, + Warn, + "empty line after outer attribute" +} + #[derive(Copy, Clone)] pub struct AttrPass; impl LintPass for AttrPass { fn get_lints(&self) -> LintArray { - lint_array!(INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE) + lint_array!(INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE, EMPTY_LINE_AFTER_OUTER_ATTR) } } @@ -230,6 +262,23 @@ fn check_attrs(cx: &LateContext, span: Span, name: &Name, attrs: &[Attribute]) { } for attr in attrs { + if attr.style == AttrStyle::Outer { + let attr_to_item_span = Span::new(attr.span.lo(), span.lo(), span.ctxt()); + + if let Some(snippet) = snippet_opt(cx, attr_to_item_span) { + let lines = snippet.split('\n').collect::>(); + if lines.iter().filter(|l| l.trim().is_empty()).count() > 1 { + span_lint( + cx, + EMPTY_LINE_AFTER_OUTER_ATTR, + attr_to_item_span, + &format!("Found an empty line after an outer attribute. Perhaps you forgot to add a '!' to make it an inner attribute?") + ); + + } + } + } + if let Some(ref values) = attr.meta_item_list() { if values.len() != 1 || attr.name().map_or(true, |n| n != "inline") { continue; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 62df7fd1058..723d15e8760 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -438,6 +438,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { attrs::DEPRECATED_SEMVER, attrs::INLINE_ALWAYS, attrs::USELESS_ATTRIBUTE, + attrs::EMPTY_LINE_AFTER_OUTER_ATTR, bit_mask::BAD_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK, bit_mask::VERBOSE_BIT_MASK, diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs new file mode 100644 index 00000000000..fa8958612f8 --- /dev/null +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -0,0 +1,21 @@ + +#![warn(empty_line_after_outer_attr)] + +// This should produce a warning +#[crate_type = "lib"] + +fn with_one_newline() { assert!(true) } + +// This should produce a warning, too +#[crate_type = "lib"] + + +fn with_two_newlines() { assert!(true) } + +// This should not produce a warning +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(missing_docs)] +fn three_attributes() { assert!(true) } + +fn main() { } diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr new file mode 100644 index 00000000000..04de89c60f6 --- /dev/null +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -0,0 +1,19 @@ +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:5:1 + | +5 | / #[crate_type = "lib"] +6 | | +7 | | fn with_one_newline() { assert!(true) } + | |_ + | + = note: `-D 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:10:1 + | +10 | / #[crate_type = "lib"] +11 | | +12 | | +13 | | fn with_two_newlines() { assert!(true) } + | |_ +