Auto merge of #8504 - xFrednet:8502-allow-lint-without-reason, r=flip1995

Add lint to detect `allow` attributes without reason

I was considering putting this lint into the pedantic group. However, that would result in countless warnings for existing projects. Having it in restriction also seems good to me 🙃 (And now I need sleep 💤 )

---

changelog: New lint [`allow_lint_without_reason`] (Requires the `lint_reasons` feature)

Closes: rust-lang/rust-clippy#8502
This commit is contained in:
bors 2022-03-04 19:23:39 +00:00
commit 48d54942f5
6 changed files with 99 additions and 1 deletions

View file

@ -3042,6 +3042,7 @@ Released 2018-09-13
<!-- lint disable no-unused-definitions -->
<!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions

View file

@ -255,7 +255,38 @@ declare_clippy_lint! {
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for attributes that allow lints without a reason.
///
/// (This requires the `lint_reasons` feature)
///
/// ### Why is this bad?
/// Allowing a lint should always have a reason. This reason should be documented to
/// ensure that others understand the reasoning
///
/// ### Example
/// Bad:
/// ```rust
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint)]
/// ```
///
/// Good:
/// ```rust
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
/// ```
#[clippy::version = "1.61.0"]
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
restriction,
"ensures that all `allow` and `expect` attributes have a reason"
}
declare_lint_pass!(Attributes => [
ALLOW_ATTRIBUTES_WITHOUT_REASON,
INLINE_ALWAYS,
DEPRECATED_SEMVER,
USELESS_ATTRIBUTE,
@ -269,6 +300,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
if is_lint_level(ident.name) {
check_clippy_lint_names(cx, ident.name, items);
}
if matches!(ident.name, sym::allow | sym::expect) {
check_lint_reason(cx, ident.name, items, attr);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
}
}
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
// Check for the feature
if !cx.tcx.sess.features_untracked().lint_reasons {
return;
}
// Check if the reason is present
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
&& let MetaItemKind::NameValue(_) = &item.kind
&& item.path == sym::reason
{
return;
}
span_lint_and_help(
cx,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
attr.span,
&format!("`{}` attribute without specifying a reason", name.as_str()),
None,
"try adding a reason at the end with `, reason = \"..\"`",
);
}
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Fn(_, _, eid) = item.kind {
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) {
}
fn is_lint_level(symbol: Symbol) -> bool {
matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid)
matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
}

View file

@ -44,6 +44,7 @@ store.register_lints(&[
assign_ops::ASSIGN_OP_PATTERN,
assign_ops::MISREFACTORED_ASSIGN_OP,
async_yields_async::ASYNC_YIELDS_ASYNC,
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
attrs::DEPRECATED_CFG_ATTR,
attrs::DEPRECATED_SEMVER,

View file

@ -8,6 +8,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(as_conversions::AS_CONVERSIONS),
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),

View file

@ -0,0 +1,14 @@
#![feature(lint_reasons)]
#![deny(clippy::allow_attributes_without_reason)]
// These should trigger the lint
#[allow(dead_code)]
#[allow(dead_code, deprecated)]
// These should be fine
#[allow(dead_code, reason = "This should be allowed")]
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
#[warn(deref_nullptr)]
#[deny(deref_nullptr)]
#[forbid(deref_nullptr)]
fn main() {}

View file

@ -0,0 +1,23 @@
error: `allow` attribute without specifying a reason
--> $DIR/allow_attributes_without_reason.rs:5:1
|
LL | #[allow(dead_code)]
| ^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/allow_attributes_without_reason.rs:2:9
|
LL | #![deny(clippy::allow_attributes_without_reason)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: try adding a reason at the end with `, reason = ".."`
error: `allow` attribute without specifying a reason
--> $DIR/allow_attributes_without_reason.rs:6:1
|
LL | #[allow(dead_code, deprecated)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try adding a reason at the end with `, reason = ".."`
error: aborting due to 2 previous errors