@@ -485,6 +485,33 @@ declare_clippy_lint! {
485485 "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
486486}
487487
488+ declare_clippy_lint ! {
489+ /// ### What it does
490+ /// Checks that an item has only one kind of attributes.
491+ ///
492+ /// ### Why is this bad?
493+ /// Having both kinds of attributes makes it more complicated to read code.
494+ ///
495+ /// ### Example
496+ /// ```no_run
497+ /// #[cfg(linux)]
498+ /// pub fn foo() {
499+ /// #![cfg(windows)]
500+ /// }
501+ /// ```
502+ /// Use instead:
503+ /// ```no_run
504+ /// #[cfg(linux)]
505+ /// #[cfg(windows)]
506+ /// pub fn foo() {
507+ /// }
508+ /// ```
509+ #[ clippy:: version = "1.78.0" ]
510+ pub MIXED_ATTRIBUTES_STYLE ,
511+ suspicious,
512+ "item has both inner and outer attributes"
513+ }
514+
488515declare_lint_pass ! ( Attributes => [
489516 ALLOW_ATTRIBUTES_WITHOUT_REASON ,
490517 INLINE_ALWAYS ,
@@ -849,11 +876,13 @@ impl_lint_pass!(EarlyAttributes => [
849876 MAYBE_MISUSED_CFG ,
850877 DEPRECATED_CLIPPY_CFG_ATTR ,
851878 UNNECESSARY_CLIPPY_CFG ,
879+ MIXED_ATTRIBUTES_STYLE ,
852880] ) ;
853881
854882impl EarlyLintPass for EarlyAttributes {
855883 fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & rustc_ast:: Item ) {
856884 check_empty_line_after_outer_attr ( cx, item) ;
885+ check_mixed_attributes ( cx, item) ;
857886 }
858887
859888 fn check_attribute ( & mut self , cx : & EarlyContext < ' _ > , attr : & Attribute ) {
@@ -867,6 +896,32 @@ impl EarlyLintPass for EarlyAttributes {
867896 extract_msrv_attr ! ( EarlyContext ) ;
868897}
869898
899+ fn check_mixed_attributes ( cx : & EarlyContext < ' _ > , item : & rustc_ast:: Item ) {
900+ let mut has_outer = false ;
901+ let mut has_inner = false ;
902+
903+ for attr in & item. attrs {
904+ if attr. span . from_expansion ( ) {
905+ continue ;
906+ }
907+ match attr. style {
908+ AttrStyle :: Inner => has_inner = true ,
909+ AttrStyle :: Outer => has_outer = true ,
910+ }
911+ }
912+ if !has_outer || !has_inner {
913+ return ;
914+ }
915+ let mut attrs_iter = item. attrs . iter ( ) . filter ( |attr| !attr. span . from_expansion ( ) ) ;
916+ let span = attrs_iter. next ( ) . unwrap ( ) . span ;
917+ span_lint (
918+ cx,
919+ MIXED_ATTRIBUTES_STYLE ,
920+ span. with_hi ( attrs_iter. last ( ) . unwrap ( ) . span . hi ( ) ) ,
921+ "item has both inner and outer attributes" ,
922+ ) ;
923+ }
924+
870925/// Check for empty lines after outer attributes.
871926///
872927/// Attributes and documentation comments are both considered outer attributes
0 commit comments