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 rust-lang/rust-clippy#16491 ---- changelog: [`allow_attributes`]: correctly detect attributes with internal whitespace
This commit is contained in:
commit
301aae6ac8
4 changed files with 28 additions and 13 deletions
|
|
@ -45,6 +45,8 @@ pub enum Pat {
|
|||
Sym(Symbol),
|
||||
/// Any decimal or hexadecimal digit depending on the location.
|
||||
Num,
|
||||
/// An attribute.
|
||||
Attr(Symbol),
|
||||
}
|
||||
|
||||
/// 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) ->
|
|||
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
|
||||
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
|
||||
Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
|
||||
Pat::Attr(sym) => {
|
||||
let start_str = start_str
|
||||
.strip_prefix("#[")
|
||||
.or_else(|| start_str.strip_prefix("#!["))
|
||||
.unwrap_or(start_str);
|
||||
start_str.trim_start().starts_with(sym.as_str())
|
||||
},
|
||||
} && match end_pat {
|
||||
Pat::Str(text) => end_str.ends_with(text),
|
||||
Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
|
||||
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
|
||||
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
|
||||
Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
|
||||
Pat::Attr(_) => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -350,18 +360,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
|
|||
AttrKind::Normal(..) => {
|
||||
if let Some(name) = attr.name() {
|
||||
// NOTE: This will likely have false positives, like `allow = 1`
|
||||
let ident_string = name.to_string();
|
||||
if attr.style == AttrStyle::Outer {
|
||||
(
|
||||
Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
|
||||
Pat::Str(""),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
|
||||
Pat::Str(""),
|
||||
)
|
||||
}
|
||||
(Pat::Attr(name), Pat::Str(""))
|
||||
} else {
|
||||
(Pat::Str("#"), Pat::Str("]"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ fn msrv_1_80() {
|
|||
let x = 1;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[ expect ( dead_code ) ]
|
||||
//~^ allow_attributes
|
||||
struct Spaced;
|
||||
|
||||
#[deny(clippy::allow_attributes)]
|
||||
fn deny_allow_attributes() -> Option<u8> {
|
||||
let allow = None;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ fn msrv_1_80() {
|
|||
let x = 1;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[ allow ( dead_code ) ]
|
||||
//~^ allow_attributes
|
||||
struct Spaced;
|
||||
|
||||
#[deny(clippy::allow_attributes)]
|
||||
fn deny_allow_attributes() -> Option<u8> {
|
||||
let allow = None;
|
||||
|
|
|
|||
|
|
@ -19,5 +19,11 @@ error: #[allow] attribute found
|
|||
LL | #[allow(unused)]
|
||||
| ^^^^^ help: replace it with: `expect`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: #[allow] attribute found
|
||||
--> tests/ui/allow_attributes.rs:67:4
|
||||
|
|
||||
LL | #[ allow ( dead_code ) ]
|
||||
| ^^^^^ help: replace it with: `expect`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue