Skip to content

Commit 301aae6

Browse files
authored
fix: allow_attributes false negative on attributes with whitespace (#16497)
The [`allow_attributes`] lint false-negatived (failed to trigger) on attributes that contained internal whitespace, such as `#[ allow ( dead_code ) ]`. This happened because `clippy_utils::is_from_proc_macro` relied on strict string matching (e.g., expecting exactly `#[allow`), which fails if there are spaces. #### Solution: I have updated `clippy_utils::is_from_proc_macro` to support flexible whitespace matching. 1. Added `Pat::Attr(Symbol)`. 2. Updated `span_matches_pat` to strip `#[` and trim validation whitespace before checking the name. Verified with a new test case in `tests/ui/allow_attributes.rs`. Fixes #16491 ---- changelog: [`allow_attributes`]: correctly detect attributes with internal whitespace
2 parents 37c127c + 992a6f6 commit 301aae6

File tree

4 files changed

+28
-13
lines changed

4 files changed

+28
-13
lines changed

clippy_utils/src/check_proc_macro.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum Pat {
4545
Sym(Symbol),
4646
/// Any decimal or hexadecimal digit depending on the location.
4747
Num,
48+
/// An attribute.
49+
Attr(Symbol),
4850
}
4951

5052
/// Checks if the start and the end of the span's text matches the patterns. This will return false
@@ -65,12 +67,20 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
6567
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
6668
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
6769
Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
70+
Pat::Attr(sym) => {
71+
let start_str = start_str
72+
.strip_prefix("#[")
73+
.or_else(|| start_str.strip_prefix("#!["))
74+
.unwrap_or(start_str);
75+
start_str.trim_start().starts_with(sym.as_str())
76+
},
6877
} && match end_pat {
6978
Pat::Str(text) => end_str.ends_with(text),
7079
Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
7180
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
7281
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
7382
Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
83+
Pat::Attr(_) => false,
7484
})
7585
})
7686
}
@@ -350,18 +360,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
350360
AttrKind::Normal(..) => {
351361
if let Some(name) = attr.name() {
352362
// NOTE: This will likely have false positives, like `allow = 1`
353-
let ident_string = name.to_string();
354-
if attr.style == AttrStyle::Outer {
355-
(
356-
Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
357-
Pat::Str(""),
358-
)
359-
} else {
360-
(
361-
Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
362-
Pat::Str(""),
363-
)
364-
}
363+
(Pat::Attr(name), Pat::Str(""))
365364
} else {
366365
(Pat::Str("#"), Pat::Str("]"))
367366
}

tests/ui/allow_attributes.fixed

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ fn msrv_1_80() {
6363
let x = 1;
6464
}
6565

66+
#[rustfmt::skip]
67+
#[ expect ( dead_code ) ]
68+
//~^ allow_attributes
69+
struct Spaced;
70+
6671
#[deny(clippy::allow_attributes)]
6772
fn deny_allow_attributes() -> Option<u8> {
6873
let allow = None;

tests/ui/allow_attributes.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ fn msrv_1_80() {
6363
let x = 1;
6464
}
6565

66+
#[rustfmt::skip]
67+
#[ allow ( dead_code ) ]
68+
//~^ allow_attributes
69+
struct Spaced;
70+
6671
#[deny(clippy::allow_attributes)]
6772
fn deny_allow_attributes() -> Option<u8> {
6873
let allow = None;

tests/ui/allow_attributes.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,11 @@ error: #[allow] attribute found
1919
LL | #[allow(unused)]
2020
| ^^^^^ help: replace it with: `expect`
2121

22-
error: aborting due to 3 previous errors
22+
error: #[allow] attribute found
23+
--> tests/ui/allow_attributes.rs:67:4
24+
|
25+
LL | #[ allow ( dead_code ) ]
26+
| ^^^^^ help: replace it with: `expect`
27+
28+
error: aborting due to 4 previous errors
2329

0 commit comments

Comments
 (0)