2
2
3
3
use crate :: reexport:: Name ;
4
4
use crate :: utils:: {
5
- first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg ,
6
- span_lint_and_then, without_block_comments,
5
+ first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help ,
6
+ span_lint_and_sugg , span_lint_and_then, without_block_comments,
7
7
} ;
8
8
use if_chain:: if_chain;
9
9
use rustc_ast:: ast:: { AttrKind , AttrStyle , Attribute , Lit , LitKind , MetaItemKind , NestedMetaItem } ;
@@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro;
17
17
use rustc_middle:: ty;
18
18
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
19
19
use rustc_span:: source_map:: Span ;
20
- use rustc_span:: symbol:: Symbol ;
20
+ use rustc_span:: symbol:: { Symbol , SymbolStr } ;
21
21
use semver:: Version ;
22
22
23
23
static UNIX_SYSTEMS : & [ & str ] = & [
@@ -182,6 +182,29 @@ declare_clippy_lint! {
182
182
"unknown_lints for scoped Clippy lints"
183
183
}
184
184
185
+ declare_clippy_lint ! {
186
+ /// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
187
+ ///
188
+ /// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
189
+ /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
190
+ ///
191
+ /// **Known problems:** None.
192
+ ///
193
+ /// **Example:**
194
+ /// Bad:
195
+ /// ```rust
196
+ /// #![deny(clippy::restriction)]
197
+ /// ```
198
+ ///
199
+ /// Good:
200
+ /// ```rust
201
+ /// #![deny(clippy::as_conversions)]
202
+ /// ```
203
+ pub BLANKET_CLIPPY_RESTRICTION_LINTS ,
204
+ style,
205
+ "enabling the complete restriction group"
206
+ }
207
+
185
208
declare_clippy_lint ! {
186
209
/// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
187
210
/// with `#[rustfmt::skip]`.
@@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [
249
272
DEPRECATED_SEMVER ,
250
273
USELESS_ATTRIBUTE ,
251
274
UNKNOWN_CLIPPY_LINTS ,
275
+ BLANKET_CLIPPY_RESTRICTION_LINTS ,
252
276
] ) ;
253
277
254
278
impl < ' tcx > LateLintPass < ' tcx > for Attributes {
255
279
fn check_attribute ( & mut self , cx : & LateContext < ' tcx > , attr : & ' tcx Attribute ) {
256
280
if let Some ( items) = & attr. meta_item_list ( ) {
257
281
if let Some ( ident) = attr. ident ( ) {
258
- match & * ident. as_str ( ) {
282
+ let ident = & * ident. as_str ( ) ;
283
+ match ident {
259
284
"allow" | "warn" | "deny" | "forbid" => {
260
- check_clippy_lint_names ( cx, items) ;
285
+ check_clippy_lint_names ( cx, ident , items) ;
261
286
} ,
262
287
_ => { } ,
263
288
}
@@ -363,38 +388,43 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
363
388
}
364
389
}
365
390
366
- #[ allow( clippy:: single_match_else) ]
367
- fn check_clippy_lint_names ( cx : & LateContext < ' _ > , items : & [ NestedMetaItem ] ) {
368
- let lint_store = cx. lints ( ) ;
369
- for lint in items {
391
+ fn check_clippy_lint_names ( cx : & LateContext < ' _ > , ident : & str , items : & [ NestedMetaItem ] ) {
392
+ fn extract_name ( lint : & NestedMetaItem ) -> Option < SymbolStr > {
370
393
if_chain ! {
371
394
if let Some ( meta_item) = lint. meta_item( ) ;
372
395
if meta_item. path. segments. len( ) > 1 ;
373
396
if let tool_name = meta_item. path. segments[ 0 ] . ident;
374
397
if tool_name. as_str( ) == "clippy" ;
375
- let name = meta_item. path. segments. last( ) . unwrap( ) . ident. name;
376
- if let CheckLintNameResult :: Tool ( Err ( ( None , _) ) ) = lint_store. check_lint_name(
377
- & name. as_str( ) ,
378
- Some ( tool_name. name) ,
379
- ) ;
398
+ let lint_name = meta_item. path. segments. last( ) . unwrap( ) . ident. name;
380
399
then {
400
+ return Some ( lint_name. as_str( ) ) ;
401
+ }
402
+ }
403
+ None
404
+ }
405
+
406
+ let lint_store = cx. lints ( ) ;
407
+ for lint in items {
408
+ if let Some ( lint_name) = extract_name ( lint) {
409
+ if let CheckLintNameResult :: Tool ( Err ( ( None , _) ) ) =
410
+ lint_store. check_lint_name ( & lint_name, Some ( sym ! ( clippy) ) )
411
+ {
381
412
span_lint_and_then (
382
413
cx,
383
414
UNKNOWN_CLIPPY_LINTS ,
384
415
lint. span ( ) ,
385
- & format!( "unknown clippy lint: clippy::{}" , name ) ,
416
+ & format ! ( "unknown clippy lint: clippy::{}" , lint_name ) ,
386
417
|diag| {
387
- let name_lower = name. as_str( ) . to_lowercase( ) ;
388
- let symbols = lint_store. get_lints( ) . iter( ) . map(
389
- |l| Symbol :: intern( & l. name_lower( ) )
390
- ) . collect:: <Vec <_>>( ) ;
391
- let sugg = find_best_match_for_name(
392
- symbols. iter( ) ,
393
- & format!( "clippy::{}" , name_lower) ,
394
- None ,
395
- ) ;
396
- if name. as_str( ) . chars( ) . any( char :: is_uppercase)
397
- && lint_store. find_lints( & format!( "clippy::{}" , name_lower) ) . is_ok( ) {
418
+ let name_lower = lint_name. to_lowercase ( ) ;
419
+ let symbols = lint_store
420
+ . get_lints ( )
421
+ . iter ( )
422
+ . map ( |l| Symbol :: intern ( & l. name_lower ( ) ) )
423
+ . collect :: < Vec < _ > > ( ) ;
424
+ let sugg = find_best_match_for_name ( symbols. iter ( ) , & format ! ( "clippy::{}" , name_lower) , None ) ;
425
+ if lint_name. chars ( ) . any ( char:: is_uppercase)
426
+ && lint_store. find_lints ( & format ! ( "clippy::{}" , name_lower) ) . is_ok ( )
427
+ {
398
428
diag. span_suggestion (
399
429
lint. span ( ) ,
400
430
"lowercase the lint name" ,
@@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
409
439
Applicability :: MachineApplicable ,
410
440
) ;
411
441
}
412
- }
442
+ } ,
443
+ ) ;
444
+ } else if lint_name == "restriction" && ident != "allow" {
445
+ span_lint_and_help (
446
+ cx,
447
+ BLANKET_CLIPPY_RESTRICTION_LINTS ,
448
+ lint. span ( ) ,
449
+ "restriction lints are not meant to be all enabled" ,
450
+ None ,
451
+ "try enabling only the lints you really need" ,
413
452
) ;
414
453
}
415
- } ;
454
+ }
416
455
}
417
456
}
418
457
@@ -442,15 +481,14 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
442
481
}
443
482
444
483
fn is_relevant_block ( cx : & LateContext < ' _ > , tables : & ty:: TypeckTables < ' _ > , block : & Block < ' _ > ) -> bool {
445
- if let Some ( stmt) = block. stmts . first ( ) {
446
- match & stmt. kind {
484
+ block. stmts . first ( ) . map_or (
485
+ block. expr . as_ref ( ) . map_or ( false , |e| is_relevant_expr ( cx, tables, e) ) ,
486
+ |stmt| match & stmt. kind {
447
487
StmtKind :: Local ( _) => true ,
448
488
StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => is_relevant_expr ( cx, tables, expr) ,
449
489
_ => false ,
450
- }
451
- } else {
452
- block. expr . as_ref ( ) . map_or ( false , |e| is_relevant_expr ( cx, tables, e) )
453
- }
490
+ } ,
491
+ )
454
492
}
455
493
456
494
fn is_relevant_expr ( cx : & LateContext < ' _ > , tables : & ty:: TypeckTables < ' _ > , expr : & Expr < ' _ > ) -> bool {
@@ -460,11 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &
460
498
ExprKind :: Ret ( None ) | ExprKind :: Break ( _, None ) => false ,
461
499
ExprKind :: Call ( path_expr, _) => {
462
500
if let ExprKind :: Path ( qpath) = & path_expr. kind {
463
- if let Some ( fun_id) = tables. qpath_res ( qpath, path_expr. hir_id ) . opt_def_id ( ) {
464
- !match_def_path ( cx, fun_id, & paths:: BEGIN_PANIC )
465
- } else {
466
- true
467
- }
501
+ tables
502
+ . qpath_res ( qpath, path_expr. hir_id )
503
+ . opt_def_id ( )
504
+ . map_or ( true , |fun_id| !match_def_path ( cx, fun_id, & paths:: BEGIN_PANIC ) )
468
505
} else {
469
506
true
470
507
}
0 commit comments