Skip to content

Commit 1bdc08a

Browse files
committed
Auto merge of #13599 - RuairidhWilliamson:proc_macro_attr, r=blyxyas
Fix allow_attributes when expanded from some macros fixes #13349 The issue here was that the start pattern being matched on the original source code was not specific enough. When using derive macros or in the issue case a `#[repr(C)]` the `#` would match the start pattern meaning that the expanded macro appeared to be unchanged and clippy would lint it. The change I made was to make the matching more specific by matching `#[ident` at the start. We still need the second string to match just the ident on its own because of things like `#[cfg_attr(panic = "unwind", allow(unused))]`. I also noticed some typos with start and end, these code paths weren't being reached so this doesn't fix anything. changelog: FP: [`allow_attributes`]: don't trigger when expanded from some macros
2 parents 15ad824 + 59ecf4d commit 1bdc08a

File tree

5 files changed

+83
-15
lines changed

5 files changed

+83
-15
lines changed

clippy_utils/src/check_proc_macro.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
6363
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
6464
} && match end_pat {
6565
Pat::Str(text) => end_str.ends_with(text),
66-
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
67-
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
66+
Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
67+
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
6868
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
6969
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
7070
})
@@ -333,26 +333,32 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
333333
match attr.kind {
334334
AttrKind::Normal(..) => {
335335
if let Some(ident) = attr.ident() {
336-
// TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
337-
// refactoring
338336
// NOTE: This will likely have false positives, like `allow = 1`
339-
(
340-
Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
341-
Pat::Str(""),
342-
)
337+
let ident_string = ident.to_string();
338+
if attr.style == AttrStyle::Outer {
339+
(
340+
Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
341+
Pat::Str(""),
342+
)
343+
} else {
344+
(
345+
Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
346+
Pat::Str(""),
347+
)
348+
}
343349
} else {
344350
(Pat::Str("#"), Pat::Str("]"))
345351
}
346352
},
347353
AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
348-
if matches!(attr.style, AttrStyle::Outer) {
354+
if attr.style == AttrStyle::Outer {
349355
(Pat::Str("///"), Pat::Str(""))
350356
} else {
351357
(Pat::Str("//!"), Pat::Str(""))
352358
}
353359
},
354360
AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
355-
if matches!(attr.style, AttrStyle::Outer) {
361+
if attr.style == AttrStyle::Outer {
356362
(Pat::Str("/**"), Pat::Str("*/"))
357363
} else {
358364
(Pat::Str("/*!"), Pat::Str("*/"))

tests/ui/allow_attributes.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@aux-build:proc_macros.rs
2+
//@aux-build:proc_macro_derive.rs
23
#![allow(unused)]
34
#![warn(clippy::allow_attributes)]
45
#![no_main]
@@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> {
6566
allow?;
6667
Some(42)
6768
}
69+
70+
// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc
71+
// macro check
72+
#[repr(transparent)]
73+
#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr
74+
struct IgnoreDerived;

tests/ui/allow_attributes.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@aux-build:proc_macros.rs
2+
//@aux-build:proc_macro_derive.rs
23
#![allow(unused)]
34
#![warn(clippy::allow_attributes)]
45
#![no_main]
@@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> {
6566
allow?;
6667
Some(42)
6768
}
69+
70+
// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc
71+
// macro check
72+
#[repr(transparent)]
73+
#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr
74+
struct IgnoreDerived;

tests/ui/allow_attributes.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: #[allow] attribute found
2-
--> tests/ui/allow_attributes.rs:12:3
2+
--> tests/ui/allow_attributes.rs:13:3
33
|
44
LL | #[allow(dead_code)]
55
| ^^^^^ help: replace it with: `expect`
@@ -8,19 +8,19 @@ LL | #[allow(dead_code)]
88
= help: to override `-D warnings` add `#[allow(clippy::allow_attributes)]`
99

1010
error: #[allow] attribute found
11-
--> tests/ui/allow_attributes.rs:21:30
11+
--> tests/ui/allow_attributes.rs:22:30
1212
|
1313
LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
1414
| ^^^^^ help: replace it with: `expect`
1515

1616
error: #[allow] attribute found
17-
--> tests/ui/allow_attributes.rs:52:7
17+
--> tests/ui/allow_attributes.rs:53:7
1818
|
1919
LL | #[allow(unused)]
2020
| ^^^^^ help: replace it with: `expect`
2121

2222
error: #[allow] attribute found
23-
--> tests/ui/allow_attributes.rs:52:7
23+
--> tests/ui/allow_attributes.rs:53:7
2424
|
2525
LL | #[allow(unused)]
2626
| ^^^^^ help: replace it with: `expect`

tests/ui/auxiliary/proc_macro_derive.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(repr128, proc_macro_quote)]
1+
#![feature(repr128, proc_macro_quote, proc_macro_span)]
22
#![allow(incomplete_features)]
33
#![allow(clippy::field_reassign_with_default)]
44
#![allow(clippy::eq_op)]
@@ -182,3 +182,51 @@ pub fn non_canonical_clone_derive(_: TokenStream) -> TokenStream {
182182
impl Copy for NonCanonicalClone {}
183183
}
184184
}
185+
186+
// Derive macro that generates the following but where all generated spans are set to the entire
187+
// input span.
188+
//
189+
// ```
190+
// #[allow(clippy::missing_const_for_fn)]
191+
// fn check() {}
192+
// ```
193+
#[proc_macro_derive(AllowLintSameSpan)]
194+
pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream {
195+
let mut iter = input.into_iter();
196+
let first = iter.next().unwrap();
197+
let last = iter.last().unwrap();
198+
let span = first.span().join(last.span()).unwrap();
199+
let span_help = |mut t: TokenTree| -> TokenTree {
200+
t.set_span(span);
201+
t
202+
};
203+
// Generate the TokenStream but setting all the spans to the entire input span
204+
<TokenStream as FromIterator<TokenTree>>::from_iter([
205+
span_help(Punct::new('#', Spacing::Alone).into()),
206+
span_help(
207+
Group::new(
208+
Delimiter::Bracket,
209+
<TokenStream as FromIterator<TokenTree>>::from_iter([
210+
Ident::new("allow", span).into(),
211+
span_help(
212+
Group::new(
213+
Delimiter::Parenthesis,
214+
<TokenStream as FromIterator<TokenTree>>::from_iter([
215+
Ident::new("clippy", span).into(),
216+
span_help(Punct::new(':', Spacing::Joint).into()),
217+
span_help(Punct::new(':', Spacing::Alone).into()),
218+
Ident::new("missing_const_for_fn", span).into(),
219+
]),
220+
)
221+
.into(),
222+
),
223+
]),
224+
)
225+
.into(),
226+
),
227+
Ident::new("fn", span).into(),
228+
Ident::new("check", span).into(),
229+
span_help(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()),
230+
span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()),
231+
])
232+
}

0 commit comments

Comments
 (0)