22
33use crate :: reexport:: Name ;
44use 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,
77} ;
88use if_chain:: if_chain;
99use rustc_ast:: ast:: { AttrKind , AttrStyle , Attribute , Lit , LitKind , MetaItemKind , NestedMetaItem } ;
@@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro;
1717use rustc_middle:: ty;
1818use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1919use rustc_span:: source_map:: Span ;
20- use rustc_span:: symbol:: Symbol ;
20+ use rustc_span:: symbol:: { Symbol , SymbolStr } ;
2121use semver:: Version ;
2222
2323static UNIX_SYSTEMS : & [ & str ] = & [
@@ -182,6 +182,29 @@ declare_clippy_lint! {
182182 "unknown_lints for scoped Clippy lints"
183183}
184184
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+
185208declare_clippy_lint ! {
186209 /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
187210 /// with `#[rustfmt::skip]`.
@@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [
249272 DEPRECATED_SEMVER ,
250273 USELESS_ATTRIBUTE ,
251274 UNKNOWN_CLIPPY_LINTS ,
275+ BLANKET_CLIPPY_RESTRICTION_LINTS ,
252276] ) ;
253277
254278impl < ' tcx > LateLintPass < ' tcx > for Attributes {
255279 fn check_attribute ( & mut self , cx : & LateContext < ' tcx > , attr : & ' tcx Attribute ) {
256280 if let Some ( items) = & attr. meta_item_list ( ) {
257281 if let Some ( ident) = attr. ident ( ) {
258- match & * ident. as_str ( ) {
282+ let ident = & * ident. as_str ( ) ;
283+ match ident {
259284 "allow" | "warn" | "deny" | "forbid" => {
260- check_clippy_lint_names ( cx, items) ;
285+ check_clippy_lint_names ( cx, ident , items) ;
261286 } ,
262287 _ => { } ,
263288 }
@@ -363,38 +388,43 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
363388 }
364389}
365390
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 > {
370393 if_chain ! {
371394 if let Some ( meta_item) = lint. meta_item( ) ;
372395 if meta_item. path. segments. len( ) > 1 ;
373396 if let tool_name = meta_item. path. segments[ 0 ] . ident;
374397 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;
380399 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+ {
381412 span_lint_and_then (
382413 cx,
383414 UNKNOWN_CLIPPY_LINTS ,
384415 lint. span ( ) ,
385- & format!( "unknown clippy lint: clippy::{}" , name ) ,
416+ & format ! ( "unknown clippy lint: clippy::{}" , lint_name ) ,
386417 |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+ {
398428 diag. span_suggestion (
399429 lint. span ( ) ,
400430 "lowercase the lint name" ,
@@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
409439 Applicability :: MachineApplicable ,
410440 ) ;
411441 }
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" ,
413452 ) ;
414453 }
415- } ;
454+ }
416455 }
417456}
418457
@@ -442,15 +481,14 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
442481}
443482
444483fn 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 {
447487 StmtKind :: Local ( _) => true ,
448488 StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => is_relevant_expr ( cx, tables, expr) ,
449489 _ => false ,
450- }
451- } else {
452- block. expr . as_ref ( ) . map_or ( false , |e| is_relevant_expr ( cx, tables, e) )
453- }
490+ } ,
491+ )
454492}
455493
456494fn 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: &
460498 ExprKind :: Ret ( None ) | ExprKind :: Break ( _, None ) => false ,
461499 ExprKind :: Call ( path_expr, _) => {
462500 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 ) )
468505 } else {
469506 true
470507 }
0 commit comments