@@ -255,7 +255,38 @@ declare_clippy_lint! {
255
255
"usage of `cfg(operating_system)` instead of `cfg(target_os = \" operating_system\" )`"
256
256
}
257
257
258
+ declare_clippy_lint ! {
259
+ /// ### What it does
260
+ /// Checks for attributes that allow lints without a reason.
261
+ ///
262
+ /// (This requires the `lint_reasons` feature)
263
+ ///
264
+ /// ### Why is this bad?
265
+ /// Allowing a lint should always have a reason. This reason should be documented to
266
+ /// ensure that others understand the reasoning
267
+ ///
268
+ /// ### Example
269
+ /// Bad:
270
+ /// ```rust
271
+ /// #![feature(lint_reasons)]
272
+ ///
273
+ /// #![allow(clippy::some_lint)]
274
+ /// ```
275
+ ///
276
+ /// Good:
277
+ /// ```rust
278
+ /// #![feature(lint_reasons)]
279
+ ///
280
+ /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
281
+ /// ```
282
+ #[ clippy:: version = "1.61.0" ]
283
+ pub ALLOW_ATTRIBUTES_WITHOUT_REASON ,
284
+ restriction,
285
+ "ensures that all `allow` and `expect` attributes have a reason"
286
+ }
287
+
258
288
declare_lint_pass ! ( Attributes => [
289
+ ALLOW_ATTRIBUTES_WITHOUT_REASON ,
259
290
INLINE_ALWAYS ,
260
291
DEPRECATED_SEMVER ,
261
292
USELESS_ATTRIBUTE ,
@@ -269,6 +300,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
269
300
if is_lint_level ( ident. name ) {
270
301
check_clippy_lint_names ( cx, ident. name , items) ;
271
302
}
303
+ if matches ! ( ident. name, sym:: allow | sym:: expect) {
304
+ check_lint_reason ( cx, ident. name , items, attr) ;
305
+ }
272
306
if items. is_empty ( ) || !attr. has_name ( sym:: deprecated) {
273
307
return ;
274
308
}
@@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
404
438
}
405
439
}
406
440
441
+ fn check_lint_reason ( cx : & LateContext < ' _ > , name : Symbol , items : & [ NestedMetaItem ] , attr : & ' _ Attribute ) {
442
+ // Check for the feature
443
+ if !cx. tcx . sess . features_untracked ( ) . lint_reasons {
444
+ return ;
445
+ }
446
+
447
+ // Check if the reason is present
448
+ if let Some ( item) = items. last ( ) . and_then ( NestedMetaItem :: meta_item)
449
+ && let MetaItemKind :: NameValue ( _) = & item. kind
450
+ && item. path == sym:: reason
451
+ {
452
+ return ;
453
+ }
454
+
455
+ span_lint_and_help (
456
+ cx,
457
+ ALLOW_ATTRIBUTES_WITHOUT_REASON ,
458
+ attr. span ,
459
+ & format ! ( "`{}` attribute without reason" , name. as_str( ) ) ,
460
+ None ,
461
+ "try adding a reason at the end with `, reason = \" ..\" `" ,
462
+ ) ;
463
+ }
464
+
407
465
fn is_relevant_item ( cx : & LateContext < ' _ > , item : & Item < ' _ > ) -> bool {
408
466
if let ItemKind :: Fn ( _, _, eid) = item. kind {
409
467
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) {
659
717
}
660
718
661
719
fn is_lint_level ( symbol : Symbol ) -> bool {
662
- matches ! ( symbol, sym:: allow | sym:: warn | sym:: deny | sym:: forbid)
720
+ matches ! ( symbol, sym:: allow | sym:: expect | sym :: warn | sym:: deny | sym:: forbid)
663
721
}
0 commit comments