Skip to content

Commit b403321

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

File tree

6 files changed

+98
-1
lines changed

6 files changed

+98
-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_lint_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_lint_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: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,36 @@ 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_LINT_WITHOUT_REASON,
284+
restriction,
285+
"ensures that all `allow` and `expect` attributes have a reason"
286+
}
287+
258288
declare_lint_pass!(Attributes => [
259289
INLINE_ALWAYS,
260290
DEPRECATED_SEMVER,
@@ -269,6 +299,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
269299
if is_lint_level(ident.name) {
270300
check_clippy_lint_names(cx, ident.name, items);
271301
}
302+
if matches!(ident.name, sym::allow | sym::expect) {
303+
check_lint_reason(cx, ident.name, items, attr);
304+
}
272305
if items.is_empty() || !attr.has_name(sym::deprecated) {
273306
return;
274307
}
@@ -404,6 +437,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
404437
}
405438
}
406439

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

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

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_LINT_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_LINT_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),

tests/ui/allow_lint_without_reason.rs

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_lint_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 reason
2+
--> $DIR/allow_lint_without_reason.rs:5:1
3+
|
4+
LL | #[allow(dead_code)]
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/allow_lint_without_reason.rs:2:9
9+
|
10+
LL | #![deny(clippy::allow_lint_without_reason)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: try adding a reason at the end with `, reason = ".."`
13+
14+
error: `allow` attribute without reason
15+
--> $DIR/allow_lint_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)