Skip to content

Commit ab6ffb6

Browse files
committed
Add lint to detect allow attributes without reason
1 parent 4417f78 commit ab6ffb6

6 files changed

+99
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,7 @@ Released 2018-09-13
30423042
<!-- lint disable no-unused-definitions -->
30433043
<!-- begin autogenerated links to lint list -->
30443044
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
3045+
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
30453046
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
30463047
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
30473048
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions

clippy_lints/src/attrs.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,38 @@ declare_clippy_lint! {
255255
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
256256
}
257257

258+
declare_clippy_lint! {
259+
/// ### What it does
260+
/// Checks for attributes that allow lints without a reason.
261+
///
262+
/// (This requires the `lint_reasons` feature)
263+
///
264+
/// ### Why is this bad?
265+
/// Allowing a lint should always have a reason. This reason should be documented to
266+
/// ensure that others understand the reasoning
267+
///
268+
/// ### Example
269+
/// Bad:
270+
/// ```rust
271+
/// #![feature(lint_reasons)]
272+
///
273+
/// #![allow(clippy::some_lint)]
274+
/// ```
275+
///
276+
/// Good:
277+
/// ```rust
278+
/// #![feature(lint_reasons)]
279+
///
280+
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
281+
/// ```
282+
#[clippy::version = "1.61.0"]
283+
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
284+
restriction,
285+
"ensures that all `allow` and `expect` attributes have a reason"
286+
}
287+
258288
declare_lint_pass!(Attributes => [
289+
ALLOW_ATTRIBUTES_WITHOUT_REASON,
259290
INLINE_ALWAYS,
260291
DEPRECATED_SEMVER,
261292
USELESS_ATTRIBUTE,
@@ -269,6 +300,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
269300
if is_lint_level(ident.name) {
270301
check_clippy_lint_names(cx, ident.name, items);
271302
}
303+
if matches!(ident.name, sym::allow | sym::expect) {
304+
check_lint_reason(cx, ident.name, items, attr);
305+
}
272306
if items.is_empty() || !attr.has_name(sym::deprecated) {
273307
return;
274308
}
@@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
404438
}
405439
}
406440

441+
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
442+
// Check for the feature
443+
if !cx.tcx.sess.features_untracked().lint_reasons {
444+
return;
445+
}
446+
447+
// Check if the reason is present
448+
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
449+
&& let MetaItemKind::NameValue(_) = &item.kind
450+
&& item.path == sym::reason
451+
{
452+
return;
453+
}
454+
455+
span_lint_and_help(
456+
cx,
457+
ALLOW_ATTRIBUTES_WITHOUT_REASON,
458+
attr.span,
459+
&format!("`{}` attribute without specifying a reason", name.as_str()),
460+
None,
461+
"try adding a reason at the end with `, reason = \"..\"`",
462+
);
463+
}
464+
407465
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
408466
if let ItemKind::Fn(_, _, eid) = item.kind {
409467
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
@@ -659,5 +717,5 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
659717
}
660718

661719
fn is_lint_level(symbol: Symbol) -> bool {
662-
matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid)
720+
matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
663721
}

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ store.register_lints(&[
4242
assign_ops::ASSIGN_OP_PATTERN,
4343
assign_ops::MISREFACTORED_ASSIGN_OP,
4444
async_yields_async::ASYNC_YIELDS_ASYNC,
45+
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
4546
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
4647
attrs::DEPRECATED_CFG_ATTR,
4748
attrs::DEPRECATED_SEMVER,

clippy_lints/src/lib.register_restriction.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
88
LintId::of(as_conversions::AS_CONVERSIONS),
99
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
1010
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
11+
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
1112
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
1213
LintId::of(create_dir::CREATE_DIR),
1314
LintId::of(dbg_macro::DBG_MACRO),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(lint_reasons)]
2+
#![deny(clippy::allow_attributes_without_reason)]
3+
4+
// These should trigger the lint
5+
#[allow(dead_code)]
6+
#[allow(dead_code, deprecated)]
7+
// These should be fine
8+
#[allow(dead_code, reason = "This should be allowed")]
9+
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
10+
#[warn(deref_nullptr)]
11+
#[deny(deref_nullptr)]
12+
#[forbid(deref_nullptr)]
13+
14+
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: `allow` attribute without specifying a reason
2+
--> $DIR/allow_attributes_without_reason.rs:5:1
3+
|
4+
LL | #[allow(dead_code)]
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/allow_attributes_without_reason.rs:2:9
9+
|
10+
LL | #![deny(clippy::allow_attributes_without_reason)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: try adding a reason at the end with `, reason = ".."`
13+
14+
error: `allow` attribute without specifying a reason
15+
--> $DIR/allow_attributes_without_reason.rs:6:1
16+
|
17+
LL | #[allow(dead_code, deprecated)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
= help: try adding a reason at the end with `, reason = ".."`
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)