Skip to content

Commit 8028427

Browse files
borsflip1995
authored andcommitted
Auto merge of rust-lang#12486 - J-ZhengLi:issue12435, r=y21
don't lint [`mixed_attributes_style`] when mixing docs and other attrs fixes: rust-lang#12435 fixes: rust-lang#12436 fixes: rust-lang#12530 --- changelog: don't lint [`mixed_attributes_style`] when mixing different kind of attrs; and move it to late pass;
1 parent 205af5d commit 8028427

File tree

8 files changed

+214
-23
lines changed

8 files changed

+214
-23
lines changed
Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,85 @@
11
use super::MIXED_ATTRIBUTES_STYLE;
22
use clippy_utils::diagnostics::span_lint;
3-
use rustc_ast::AttrStyle;
4-
use rustc_lint::EarlyContext;
3+
use rustc_ast::{AttrKind, AttrStyle, Attribute};
4+
use rustc_data_structures::fx::FxHashSet;
5+
use rustc_lint::{LateContext, LintContext};
6+
use rustc_span::source_map::SourceMap;
7+
use rustc_span::{SourceFile, Span, Symbol};
8+
use std::sync::Arc;
59

6-
pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
7-
let mut has_outer = false;
8-
let mut has_inner = false;
10+
#[derive(Hash, PartialEq, Eq)]
11+
enum SimpleAttrKind {
12+
Doc,
13+
/// A normal attribute, with its name symbols.
14+
Normal(Vec<Symbol>),
15+
}
16+
17+
impl From<&AttrKind> for SimpleAttrKind {
18+
fn from(value: &AttrKind) -> Self {
19+
match value {
20+
AttrKind::Normal(attr) => {
21+
let path_symbols = attr
22+
.item
23+
.path
24+
.segments
25+
.iter()
26+
.map(|seg| seg.ident.name)
27+
.collect::<Vec<_>>();
28+
Self::Normal(path_symbols)
29+
},
30+
AttrKind::DocComment(..) => Self::Doc,
31+
}
32+
}
33+
}
34+
35+
pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
36+
let mut inner_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default();
37+
let mut outer_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default();
38+
39+
let source_map = cx.sess().source_map();
40+
let item_src = source_map.lookup_source_file(item_span.lo());
941

10-
for attr in &item.attrs {
11-
if attr.span.from_expansion() {
42+
for attr in attrs {
43+
if attr.span.from_expansion() || !attr_in_same_src_as_item(source_map, &item_src, attr.span) {
1244
continue;
1345
}
46+
47+
let kind: SimpleAttrKind = (&attr.kind).into();
1448
match attr.style {
15-
AttrStyle::Inner => has_inner = true,
16-
AttrStyle::Outer => has_outer = true,
17-
}
49+
AttrStyle::Inner => {
50+
if outer_attr_kind.contains(&kind) {
51+
lint_mixed_attrs(cx, attrs);
52+
return;
53+
}
54+
inner_attr_kind.insert(kind);
55+
},
56+
AttrStyle::Outer => {
57+
if inner_attr_kind.contains(&kind) {
58+
lint_mixed_attrs(cx, attrs);
59+
return;
60+
}
61+
outer_attr_kind.insert(kind);
62+
},
63+
};
1864
}
19-
if !has_outer || !has_inner {
65+
}
66+
67+
fn lint_mixed_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) {
68+
let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion());
69+
let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) {
70+
first.span.with_hi(last.span.hi())
71+
} else {
2072
return;
21-
}
22-
let mut attrs_iter = item.attrs.iter().filter(|attr| !attr.span.from_expansion());
23-
let span = attrs_iter.next().unwrap().span;
73+
};
2474
span_lint(
2575
cx,
2676
MIXED_ATTRIBUTES_STYLE,
27-
span.with_hi(attrs_iter.last().unwrap().span.hi()),
77+
span,
2878
"item has both inner and outer attributes",
2979
);
3080
}
81+
82+
fn attr_in_same_src_as_item(source_map: &SourceMap, item_src: &Arc<SourceFile>, attr_span: Span) -> bool {
83+
let attr_src = source_map.lookup_source_file(attr_span.lo());
84+
Arc::ptr_eq(item_src, &attr_src)
85+
}

src/tools/clippy/clippy_lints/src/attrs/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,20 @@ declare_clippy_lint! {
464464

465465
declare_clippy_lint! {
466466
/// ### What it does
467-
/// Checks that an item has only one kind of attributes.
467+
/// Checks for items that have the same kind of attributes with mixed styles (inner/outer).
468468
///
469469
/// ### Why is this bad?
470-
/// Having both kinds of attributes makes it more complicated to read code.
470+
/// Having both style of said attributes makes it more complicated to read code.
471+
///
472+
/// ### Known problems
473+
/// This lint currently has false-negatives when mixing same attributes
474+
/// but they have different path symbols, for example:
475+
/// ```ignore
476+
/// #[custom_attribute]
477+
/// pub fn foo() {
478+
/// #![my_crate::custom_attribute]
479+
/// }
480+
/// ```
471481
///
472482
/// ### Example
473483
/// ```no_run
@@ -496,6 +506,7 @@ declare_lint_pass!(Attributes => [
496506
USELESS_ATTRIBUTE,
497507
BLANKET_CLIPPY_RESTRICTION_LINTS,
498508
SHOULD_PANIC_WITHOUT_EXPECT,
509+
MIXED_ATTRIBUTES_STYLE,
499510
]);
500511

501512
impl<'tcx> LateLintPass<'tcx> for Attributes {
@@ -539,6 +550,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
539550
ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs),
540551
_ => {},
541552
}
553+
mixed_attributes_style::check(cx, item.span, attrs);
542554
}
543555

544556
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@@ -567,13 +579,11 @@ impl_lint_pass!(EarlyAttributes => [
567579
MAYBE_MISUSED_CFG,
568580
DEPRECATED_CLIPPY_CFG_ATTR,
569581
UNNECESSARY_CLIPPY_CFG,
570-
MIXED_ATTRIBUTES_STYLE,
571582
]);
572583

573584
impl EarlyLintPass for EarlyAttributes {
574585
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
575586
empty_line_after::check(cx, item);
576-
mixed_attributes_style::check(cx, item);
577587
}
578588

579589
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {

src/tools/clippy/tests/ui/mixed_attributes_style.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
//@aux-build:proc_macro_attr.rs
2+
//@compile-flags: --test --cfg dummy_cfg
3+
#![feature(custom_inner_attributes)]
14
#![warn(clippy::mixed_attributes_style)]
25

6+
#[macro_use]
7+
extern crate proc_macro_attr;
8+
39
#[allow(unused)] //~ ERROR: item has both inner and outer attributes
410
fn foo1() {
511
#![allow(unused)]
@@ -37,3 +43,57 @@ mod bar {
3743
fn main() {
3844
// test code goes here
3945
}
46+
47+
// issue #12435
48+
#[cfg(test)]
49+
mod tests {
50+
//! Module doc, don't lint
51+
}
52+
#[allow(unused)]
53+
mod baz {
54+
//! Module doc, don't lint
55+
const FOO: u8 = 0;
56+
}
57+
/// Module doc, don't lint
58+
mod quz {
59+
#![allow(unused)]
60+
}
61+
62+
mod issue_12530 {
63+
// don't lint different attributes entirely
64+
#[cfg(test)]
65+
mod tests {
66+
#![allow(clippy::unreadable_literal)]
67+
68+
#[allow(dead_code)] //~ ERROR: item has both inner and outer attributes
69+
mod inner_mod {
70+
#![allow(dead_code)]
71+
}
72+
}
73+
#[cfg(dummy_cfg)]
74+
mod another_mod {
75+
#![allow(clippy::question_mark)]
76+
}
77+
/// Nested mod
78+
mod nested_mod {
79+
#[allow(dead_code)] //~ ERROR: item has both inner and outer attributes
80+
mod inner_mod {
81+
#![allow(dead_code)]
82+
}
83+
}
84+
/// Nested mod //~ ERROR: item has both inner and outer attributes
85+
#[allow(unused)]
86+
mod nest_mod_2 {
87+
#![allow(unused)]
88+
89+
#[allow(dead_code)] //~ ERROR: item has both inner and outer attributes
90+
mod inner_mod {
91+
#![allow(dead_code)]
92+
}
93+
}
94+
// Different path symbols - Known FN
95+
#[dummy]
96+
fn use_dummy() {
97+
#![proc_macro_attr::dummy]
98+
}
99+
}
Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: item has both inner and outer attributes
2-
--> tests/ui/mixed_attributes_style.rs:3:1
2+
--> tests/ui/mixed_attributes_style.rs:9:1
33
|
44
LL | / #[allow(unused)]
55
LL | | fn foo1() {
@@ -10,7 +10,7 @@ LL | | #![allow(unused)]
1010
= help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]`
1111

1212
error: item has both inner and outer attributes
13-
--> tests/ui/mixed_attributes_style.rs:17:1
13+
--> tests/ui/mixed_attributes_style.rs:23:1
1414
|
1515
LL | / /// linux
1616
LL | |
@@ -19,12 +19,45 @@ LL | | //! windows
1919
| |_______________^
2020

2121
error: item has both inner and outer attributes
22-
--> tests/ui/mixed_attributes_style.rs:32:1
22+
--> tests/ui/mixed_attributes_style.rs:38:1
2323
|
2424
LL | / #[allow(unused)]
2525
LL | | mod bar {
2626
LL | | #![allow(unused)]
2727
| |_____________________^
2828

29-
error: aborting due to 3 previous errors
29+
error: item has both inner and outer attributes
30+
--> tests/ui/mixed_attributes_style.rs:68:9
31+
|
32+
LL | / #[allow(dead_code)]
33+
LL | | mod inner_mod {
34+
LL | | #![allow(dead_code)]
35+
| |________________________________^
36+
37+
error: item has both inner and outer attributes
38+
--> tests/ui/mixed_attributes_style.rs:79:9
39+
|
40+
LL | / #[allow(dead_code)]
41+
LL | | mod inner_mod {
42+
LL | | #![allow(dead_code)]
43+
| |________________________________^
44+
45+
error: item has both inner and outer attributes
46+
--> tests/ui/mixed_attributes_style.rs:84:5
47+
|
48+
LL | / /// Nested mod
49+
LL | | #[allow(unused)]
50+
LL | | mod nest_mod_2 {
51+
LL | | #![allow(unused)]
52+
| |_________________________^
53+
54+
error: item has both inner and outer attributes
55+
--> tests/ui/mixed_attributes_style.rs:89:9
56+
|
57+
LL | / #[allow(dead_code)]
58+
LL | | mod inner_mod {
59+
LL | | #![allow(dead_code)]
60+
| |________________________________^
61+
62+
error: aborting due to 7 previous errors
3063

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Module level doc
2+
3+
#![allow(dead_code)]
4+
5+
#[allow(unused)]
6+
//~^ ERROR: item has both inner and outer attributes
7+
mod foo {
8+
#![allow(dead_code)]
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// issue 12436
2+
#![allow(clippy::mixed_attributes_style)]
3+
4+
#[path = "auxiliary/submodule.rs"]
5+
mod submodule;
6+
7+
fn main() {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#[path = "auxiliary/submodule.rs"] // don't lint.
2+
/// This doc comment should not lint, it could be used to add context to the original module doc
3+
mod submodule;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: item has both inner and outer attributes
2+
--> tests/ui/mixed_attributes_style/auxiliary/submodule.rs:5:1
3+
|
4+
LL | / #[allow(unused)]
5+
LL | |
6+
LL | | mod foo {
7+
LL | | #![allow(dead_code)]
8+
| |________________________^
9+
|
10+
= note: `-D clippy::mixed-attributes-style` implied by `-D warnings`
11+
= help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]`
12+
13+
error: aborting due to 1 previous error
14+

0 commit comments

Comments
 (0)