1 use ast::{AttrStyle, Attribute}; 2 use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro}; 3 use rustc_ast as ast; 4 use rustc_errors::Applicability; 5 use rustc_lint::{LateContext, LateLintPass, LintContext}; 6 use rustc_middle::lint::in_external_macro; 7 use rustc_session::{declare_lint_pass, declare_tool_lint}; 8 9 declare_clippy_lint! { 10 /// Checks for usage of the `#[allow]` attribute and suggests replacing it with 11 /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) 12 /// 13 /// The expect attribute is still unstable and requires the `lint_reasons` 14 /// on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to 15 /// the crate root. 16 /// 17 /// This lint only warns outer attributes (`#[allow]`), as inner attributes 18 /// (`#![allow]`) are usually used to enable or disable lints on a global scale. 19 /// 20 /// ### Why is this bad? 21 /// 22 /// `#[expect]` attributes suppress the lint emission, but emit a warning, if 23 /// the expectation is unfulfilled. This can be useful to be notified when the 24 /// lint is no longer triggered. 25 /// 26 /// ### Example 27 /// ```rust,ignore 28 /// #[allow(unused_mut)] 29 /// fn foo() -> usize { 30 /// let mut a = Vec::new(); 31 /// a.len() 32 /// } 33 /// ``` 34 /// Use instead: 35 /// ```rust,ignore 36 /// #![feature(lint_reasons)] 37 /// #[expect(unused_mut)] 38 /// fn foo() -> usize { 39 /// let mut a = Vec::new(); 40 /// a.len() 41 /// } 42 /// ``` 43 #[clippy::version = "1.70.0"] 44 pub ALLOW_ATTRIBUTES, 45 restriction, 46 "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." 47 } 48 49 declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]); 50 51 impl LateLintPass<'_> for AllowAttribute { 52 // Separate each crate's features. check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute)53 fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) { 54 if_chain! { 55 if !in_external_macro(cx.sess(), attr.span); 56 if cx.tcx.features().lint_reasons; 57 if let AttrStyle::Outer = attr.style; 58 if let Some(ident) = attr.ident(); 59 if ident.name == rustc_span::symbol::sym::allow; 60 if !is_from_proc_macro(cx, &attr); 61 then { 62 span_lint_and_sugg( 63 cx, 64 ALLOW_ATTRIBUTES, 65 ident.span, 66 "#[allow] attribute found", 67 "replace it with", 68 "expect".into(), 69 Applicability::MachineApplicable, 70 ); 71 } 72 } 73 } 74 } 75