Skip to content

Commit 1841661

Browse files
committed
Auto merge of #10869 - Centri3:allow_attributes, r=Manishearth
[`allow_attributes`, `allow_attributes_without_reason`]: Ignore attributes from procedural macros I use `lint_reasons` and `clap`, which is a bit overzealous when it comes to preventing warnings in its macros; it uses a ton of allow attributes on everything to, as ironic as it is, silence warnings. These two now ignore anything from procedural macros. PS, I think `allow_attributes.rs` should be merged with `attrs.rs` in the future. fixes #10377 changelog: [`allow_attributes`, `allow_attributes_without_reason`]: Ignore attributes from procedural macros
2 parents 4886937 + b469e8c commit 1841661

9 files changed

+166
-24
lines changed

clippy_lints/src/allow_attributes.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use ast::AttrStyle;
2-
use clippy_utils::diagnostics::span_lint_and_sugg;
1+
use ast::{AttrStyle, Attribute};
2+
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro};
33
use rustc_ast as ast;
44
use rustc_errors::Applicability;
55
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -50,13 +50,14 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
5050

5151
impl LateLintPass<'_> for AllowAttribute {
5252
// Separate each crate's features.
53-
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
53+
fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
5454
if_chain! {
5555
if !in_external_macro(cx.sess(), attr.span);
5656
if cx.tcx.features().lint_reasons;
5757
if let AttrStyle::Outer = attr.style;
5858
if let Some(ident) = attr.ident();
5959
if ident.name == rustc_span::symbol::sym::allow;
60+
if !is_from_proc_macro(cx, &attr);
6061
then {
6162
span_lint_and_sugg(
6263
cx,

clippy_lints/src/attrs.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! checks for attributes
22
3-
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
43
use clippy_utils::macros::{is_panic, macro_backtrace};
54
use clippy_utils::msrvs::{self, Msrv};
65
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
6+
use clippy_utils::{
7+
diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then},
8+
is_from_proc_macro,
9+
};
710
use if_chain::if_chain;
811
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
912
use rustc_errors::Applicability;
@@ -566,7 +569,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
566569
}
567570
}
568571

569-
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
572+
fn check_lint_reason<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
570573
// Check for the feature
571574
if !cx.tcx.features().lint_reasons {
572575
return;
@@ -581,7 +584,7 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
581584
}
582585

583586
// Check if the attribute is in an external macro and therefore out of the developer's control
584-
if in_external_macro(cx.sess(), attr.span) {
587+
if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) {
585588
return;
586589
}
587590

clippy_utils/src/check_proc_macro.rs

+63-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
//! code was written, and check if the span contains that text. Note this will only work correctly
1313
//! if the span is not from a `macro_rules` based macro.
1414
15-
use rustc_ast::ast::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
15+
use rustc_ast::{
16+
ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy},
17+
token::CommentKind,
18+
AttrStyle,
19+
};
1620
use rustc_hir::{
1721
intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId,
1822
Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, Node, QPath, TraitItem,
@@ -25,12 +29,16 @@ use rustc_span::{Span, Symbol};
2529
use rustc_target::spec::abi::Abi;
2630

2731
/// The search pattern to look for. Used by `span_matches_pat`
28-
#[derive(Clone, Copy)]
32+
#[derive(Clone)]
2933
pub enum Pat {
3034
/// A single string.
3135
Str(&'static str),
36+
/// A single string.
37+
OwnedStr(String),
3238
/// Any of the given strings.
3339
MultiStr(&'static [&'static str]),
40+
/// Any of the given strings.
41+
OwnedMultiStr(Vec<String>),
3442
/// The string representation of the symbol.
3543
Sym(Symbol),
3644
/// Any decimal or hexadecimal digit depending on the location.
@@ -51,12 +59,16 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
5159
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
5260
(match start_pat {
5361
Pat::Str(text) => start_str.starts_with(text),
62+
Pat::OwnedStr(text) => start_str.starts_with(&text),
5463
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
64+
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
5565
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
5666
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
5767
} && match end_pat {
5868
Pat::Str(text) => end_str.ends_with(text),
69+
Pat::OwnedStr(text) => end_str.starts_with(&text),
5970
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
71+
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
6072
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
6173
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
6274
})
@@ -271,6 +283,42 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
271283
(start_pat, end_pat)
272284
}
273285

286+
fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
287+
match attr.kind {
288+
AttrKind::Normal(..) => {
289+
let mut pat = if matches!(attr.style, AttrStyle::Outer) {
290+
(Pat::Str("#["), Pat::Str("]"))
291+
} else {
292+
(Pat::Str("#!["), Pat::Str("]"))
293+
};
294+
295+
if let Some(ident) = attr.ident() && let Pat::Str(old_pat) = pat.0 {
296+
// TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
297+
// refactoring
298+
// NOTE: This will likely have false positives, like `allow = 1`
299+
pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]);
300+
pat.1 = Pat::Str("");
301+
}
302+
303+
pat
304+
},
305+
AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
306+
if matches!(attr.style, AttrStyle::Outer) {
307+
(Pat::Str("///"), Pat::Str(""))
308+
} else {
309+
(Pat::Str("//!"), Pat::Str(""))
310+
}
311+
},
312+
AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
313+
if matches!(attr.style, AttrStyle::Outer) {
314+
(Pat::Str("/**"), Pat::Str("*/"))
315+
} else {
316+
(Pat::Str("/*!"), Pat::Str("*/"))
317+
}
318+
},
319+
}
320+
}
321+
274322
pub trait WithSearchPat {
275323
type Context: LintContext;
276324
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
@@ -310,6 +358,19 @@ impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
310358
}
311359
}
312360

361+
// `Attribute` does not have the `hir` associated lifetime, so we cannot use the macro
362+
impl<'cx> WithSearchPat for &'cx Attribute {
363+
type Context = LateContext<'cx>;
364+
365+
fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
366+
attr_search_pat(self)
367+
}
368+
369+
fn span(&self) -> Span {
370+
self.span
371+
}
372+
}
373+
313374
/// Checks if the item likely came from a proc-macro.
314375
///
315376
/// This should be called after `in_external_macro` and the initial pattern matching of the ast as

tests/ui/allow_attributes.fixed

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//@run-rustfix
2+
//@aux-build:proc_macros.rs
23
#![allow(unused)]
34
#![warn(clippy::allow_attributes)]
45
#![feature(lint_reasons)]
6+
#![no_main]
57

6-
fn main() {}
8+
extern crate proc_macros;
9+
use proc_macros::{external, with_span};
710

811
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
912

@@ -20,6 +23,21 @@ struct T4;
2023
#[cfg_attr(panic = "unwind", expect(dead_code))]
2124
struct CfgT;
2225

26+
fn ignore_external() {
27+
external! {
28+
#[allow(clippy::needless_borrow)] // Should not lint
29+
fn a() {}
30+
}
31+
}
32+
33+
fn ignore_proc_macro() {
34+
with_span! {
35+
span
36+
#[allow(clippy::needless_borrow)] // Should not lint
37+
fn a() {}
38+
}
39+
}
40+
2341
fn ignore_inner_attr() {
2442
#![allow(unused)] // Should not lint
2543
}

tests/ui/allow_attributes.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//@run-rustfix
2+
//@aux-build:proc_macros.rs
23
#![allow(unused)]
34
#![warn(clippy::allow_attributes)]
45
#![feature(lint_reasons)]
6+
#![no_main]
57

6-
fn main() {}
8+
extern crate proc_macros;
9+
use proc_macros::{external, with_span};
710

811
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
912

@@ -20,6 +23,21 @@ struct T4;
2023
#[cfg_attr(panic = "unwind", allow(dead_code))]
2124
struct CfgT;
2225

26+
fn ignore_external() {
27+
external! {
28+
#[allow(clippy::needless_borrow)] // Should not lint
29+
fn a() {}
30+
}
31+
}
32+
33+
fn ignore_proc_macro() {
34+
with_span! {
35+
span
36+
#[allow(clippy::needless_borrow)] // Should not lint
37+
fn a() {}
38+
}
39+
}
40+
2341
fn ignore_inner_attr() {
2442
#![allow(unused)] // Should not lint
2543
}

tests/ui/allow_attributes.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
error: #[allow] attribute found
2-
--> $DIR/allow_attributes.rs:11:3
2+
--> $DIR/allow_attributes.rs:14:3
33
|
44
LL | #[allow(dead_code)]
55
| ^^^^^ help: replace it with: `expect`
66
|
77
= note: `-D clippy::allow-attributes` implied by `-D warnings`
88

99
error: #[allow] attribute found
10-
--> $DIR/allow_attributes.rs:20:30
10+
--> $DIR/allow_attributes.rs:23:30
1111
|
1212
LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
1313
| ^^^^^ help: replace it with: `expect`

tests/ui/allow_attributes_false_positive.rs

-5
This file was deleted.
+31-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,44 @@
1+
//@aux-build:proc_macros.rs
12
#![feature(lint_reasons)]
23
#![deny(clippy::allow_attributes_without_reason)]
4+
#![allow(unfulfilled_lint_expectations)]
5+
6+
extern crate proc_macros;
7+
use proc_macros::{external, with_span};
38

49
// These should trigger the lint
510
#[allow(dead_code)]
611
#[allow(dead_code, deprecated)]
12+
#[expect(dead_code)]
713
// These should be fine
814
#[allow(dead_code, reason = "This should be allowed")]
915
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
1016
#[warn(deref_nullptr)]
1117
#[deny(deref_nullptr)]
1218
#[forbid(deref_nullptr)]
1319

14-
fn main() {}
20+
fn main() {
21+
external! {
22+
#[allow(dead_code)]
23+
fn a() {}
24+
}
25+
with_span! {
26+
span
27+
#[allow(dead_code)]
28+
fn b() {}
29+
}
30+
}
31+
32+
// Make sure this is not triggered on `?` desugaring
33+
34+
pub fn trigger_fp_option() -> Option<()> {
35+
Some(())?;
36+
None?;
37+
Some(())
38+
}
39+
40+
pub fn trigger_fp_result() -> Result<(), &'static str> {
41+
Ok(())?;
42+
Err("asdf")?;
43+
Ok(())
44+
}
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
11
error: `allow` attribute without specifying a reason
2-
--> $DIR/allow_attributes_without_reason.rs:5:1
2+
--> $DIR/allow_attributes_without_reason.rs:4:1
33
|
4-
LL | #[allow(dead_code)]
5-
| ^^^^^^^^^^^^^^^^^^^
4+
LL | #![allow(unfulfilled_lint_expectations)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= help: try adding a reason at the end with `, reason = ".."`
88
note: the lint level is defined here
9-
--> $DIR/allow_attributes_without_reason.rs:2:9
9+
--> $DIR/allow_attributes_without_reason.rs:3:9
1010
|
1111
LL | #![deny(clippy::allow_attributes_without_reason)]
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1313

1414
error: `allow` attribute without specifying a reason
15-
--> $DIR/allow_attributes_without_reason.rs:6:1
15+
--> $DIR/allow_attributes_without_reason.rs:10:1
16+
|
17+
LL | #[allow(dead_code)]
18+
| ^^^^^^^^^^^^^^^^^^^
19+
|
20+
= help: try adding a reason at the end with `, reason = ".."`
21+
22+
error: `allow` attribute without specifying a reason
23+
--> $DIR/allow_attributes_without_reason.rs:11:1
1624
|
1725
LL | #[allow(dead_code, deprecated)]
1826
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1927
|
2028
= help: try adding a reason at the end with `, reason = ".."`
2129

22-
error: aborting due to 2 previous errors
30+
error: `expect` attribute without specifying a reason
31+
--> $DIR/allow_attributes_without_reason.rs:12:1
32+
|
33+
LL | #[expect(dead_code)]
34+
| ^^^^^^^^^^^^^^^^^^^^
35+
|
36+
= help: try adding a reason at the end with `, reason = ".."`
37+
38+
error: aborting due to 4 previous errors
2339

0 commit comments

Comments
 (0)