94 lines
3.3 KiB
Rust
94 lines
3.3 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|
|
}
|