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