Skip to content

Commit 0f1474e

Browse files
committed
Add allow_attribute lint
1 parent 8e1dd06 commit 0f1474e

File tree

7 files changed

+145
-0
lines changed

7 files changed

+145
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4382,6 +4382,7 @@ Released 2018-09-13
43824382
<!-- begin autogenerated links to lint list -->
43834383
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
43844384
[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
4385+
[`allow_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attribute
43854386
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
43864387
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
43874388
[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range

clippy_lints/src/allow_attribute.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use ast::{AttrStyle, MetaItemKind};
2+
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
3+
use rustc_ast as ast;
4+
use rustc_errors::Applicability;
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::{declare_tool_lint, impl_lint_pass};
7+
use rustc_span::{symbol::Ident, BytePos};
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Detects uses of the `#[allow]` attribute and suggests to replace it with the new `#[expect]` attribute implemented by `#![feature(lint_reasons)]` ([RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
12+
/// ### Why is this bad?
13+
/// Using `#[allow]` isn't bad, but `#[expect]` may be preferred as it lints if the code **doesn't** produce a warning.
14+
/// ### Example
15+
/// ```rust
16+
/// #[allow(unused_mut)]
17+
/// fn foo() -> usize {
18+
/// let mut a = Vec::new();
19+
/// a.len()
20+
///}
21+
/// ```
22+
/// Use instead:
23+
/// ```rust
24+
/// #[expect(unused_mut)]
25+
/// fn foo() -> usize {
26+
/// let mut a = Vec::new();
27+
/// a.len()
28+
/// }
29+
/// ```
30+
#[clippy::version = "1.69.0"]
31+
pub ALLOW_ATTRIBUTE,
32+
restriction,
33+
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
34+
}
35+
36+
pub struct AllowAttribute {
37+
pub lint_reasons_active: bool,
38+
}
39+
40+
impl_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTE]);
41+
42+
impl LateLintPass<'_> for AllowAttribute {
43+
// Separate each crate's features.
44+
fn check_crate_post(&mut self, _: &LateContext<'_>) {
45+
self.lint_reasons_active = false;
46+
}
47+
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
48+
// Check inner attributes
49+
50+
if_chain! {
51+
if let AttrStyle::Inner = attr.style;
52+
if attr.ident()
53+
.unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident.
54+
.name == sym!(feature);
55+
if let ast::AttrKind::Normal(normal) = &attr.kind;
56+
if let Some(MetaItemKind::List(list)) = normal.item.meta_kind();
57+
if list[0].ident().unwrap().name == sym!(lint_reasons);
58+
then {
59+
self.lint_reasons_active = true;
60+
}
61+
}
62+
63+
// Check outer attributes
64+
65+
if_chain! {
66+
if let AttrStyle::Outer = attr.style;
67+
if attr.ident()
68+
.unwrap_or(Ident::with_dummy_span(sym!(empty))) // Will not trigger if doesn't have an ident.
69+
.name == sym!(allow);
70+
if self.lint_reasons_active;
71+
then {
72+
span_lint_and_sugg(
73+
cx,
74+
ALLOW_ATTRIBUTE,
75+
attr.span,
76+
"#[allow] attribute found",
77+
"replace it with",
78+
format!("#[expect{})]", snippet(
79+
cx,
80+
attr.ident().unwrap().span
81+
.with_lo(
82+
attr.ident().unwrap().span.hi() + BytePos(2) // Cut [(
83+
)
84+
.with_hi(
85+
attr.meta().unwrap().span.hi() - BytePos(2) // Cut )]
86+
)
87+
, "...")), Applicability::MachineApplicable);
88+
}
89+
}
90+
}
91+
}

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
3535
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
3636
#[cfg(feature = "internal")]
3737
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
38+
crate::allow_attribute::ALLOW_ATTRIBUTE_INFO,
3839
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
3940
crate::approx_const::APPROX_CONSTANT_INFO,
4041
crate::as_conversions::AS_CONVERSIONS_INFO,

clippy_lints/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ mod declared_lints;
6767
mod renamed_lints;
6868

6969
// begin lints modules, do not remove this comment, it’s used in `update_lints`
70+
mod allow_attribute;
7071
mod almost_complete_range;
7172
mod approx_const;
7273
mod as_conversions;
@@ -933,6 +934,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
933934
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
934935
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
935936
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
937+
store.register_late_pass(|_| {
938+
Box::new(allow_attribute::AllowAttribute {
939+
lint_reasons_active: false,
940+
})
941+
});
936942
// add lints here, do not remove this comment, it's used in `new_lint`
937943
}
938944

tests/ui/allow_attribute.fixed

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::allow_attribute)]
4+
#![feature(lint_reasons)]
5+
6+
fn main() {}
7+
8+
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
9+
10+
// Should lint
11+
#[expect(dead_code)]
12+
struct T1;
13+
14+
struct T2; // Should not lint
15+
#[deny(clippy::needless_borrow)] // Should not lint
16+
struct T3;
17+
#[warn(clippy::needless_borrow)] // Should not lint
18+
struct T4;

tests/ui/allow_attribute.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::allow_attribute)]
4+
#![feature(lint_reasons)]
5+
6+
fn main() {}
7+
8+
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
9+
10+
// Should lint
11+
#[allow(dead_code)]
12+
struct T1;
13+
14+
struct T2; // Should not lint
15+
#[deny(clippy::needless_borrow)] // Should not lint
16+
struct T3;
17+
#[warn(clippy::needless_borrow)] // Should not lint
18+
struct T4;

tests/ui/allow_attribute.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: #[allow] attribute found
2+
--> $DIR/allow_attribute.rs:11:1
3+
|
4+
LL | #[allow(dead_code)]
5+
| ^^^^^^^^^^^^^^^^^^^ help: replace it with: `#[expect(dead_code)]`
6+
|
7+
= note: `-D clippy::allow-attribute` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)