1+ use crate :: utils:: SpanlessEq ;
12use crate :: utils:: {
2- is_expn_of, match_def_path, match_type, method_calls, paths, span_lint , span_lint_and_help , span_lint_and_sugg ,
3- walk_ptrs_ty,
3+ is_expn_of, match_def_path, match_qpath , match_type, method_calls, paths, snippet , span_lint , span_lint_and_help ,
4+ span_lint_and_sugg , walk_ptrs_ty,
45} ;
56use if_chain:: if_chain;
67use rustc_ast:: ast:: { Crate as AstCrate , ItemKind , LitKind , Name , NodeId } ;
@@ -10,13 +11,15 @@ use rustc_errors::Applicability;
1011use rustc_hir as hir;
1112use rustc_hir:: def:: { DefKind , Res } ;
1213use rustc_hir:: intravisit:: { walk_expr, NestedVisitorMap , Visitor } ;
13- use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Path , Ty , TyKind } ;
14+ use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Path , StmtKind , Ty , TyKind } ;
1415use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
1516use rustc_middle:: hir:: map:: Map ;
1617use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
1718use rustc_span:: source_map:: { Span , Spanned } ;
1819use rustc_span:: symbol:: SymbolStr ;
1920
21+ use std:: borrow:: { Borrow , Cow } ;
22+
2023declare_clippy_lint ! {
2124 /// **What it does:** Checks for various things we like to keep tidy in clippy.
2225 ///
@@ -142,6 +145,67 @@ declare_clippy_lint! {
142145 "found 'default lint description' in a lint declaration"
143146}
144147
148+ declare_clippy_lint ! {
149+ /// **What it does:** Lints `span_lint_and_then` function calls, where the
150+ /// closure argument has only one statement and that statement is a method
151+ /// call to `span_suggestion`, `span_help`, `span_note` (using the same
152+ /// span), `help` or `note`.
153+ ///
154+ /// These usages of `span_lint_and_then` should be replaced with one of the
155+ /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
156+ /// `span_lint_and_note`.
157+ ///
158+ /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more
159+ /// convenient, readable and less error prone.
160+ ///
161+ /// **Known problems:** None
162+ ///
163+ /// *Example:**
164+ /// Bad:
165+ /// ```rust,ignore
166+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
167+ /// db.span_suggestion(
168+ /// expr.span,
169+ /// help_msg,
170+ /// sugg.to_string(),
171+ /// Applicability::MachineApplicable,
172+ /// );
173+ /// });
174+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
175+ /// db.span_help(expr.span, help_msg);
176+ /// });
177+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
178+ /// db.help(help_msg);
179+ /// });
180+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
181+ /// db.span_note(expr.span, note_msg);
182+ /// });
183+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
184+ /// db.note(note_msg);
185+ /// });
186+ /// ```
187+ ///
188+ /// Good:
189+ /// ```rust,ignore
190+ /// span_lint_and_sugg(
191+ /// cx,
192+ /// TEST_LINT,
193+ /// expr.span,
194+ /// lint_msg,
195+ /// help_msg,
196+ /// sugg.to_string(),
197+ /// Applicability::MachineApplicable,
198+ /// );
199+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, help_msg);
200+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, help_msg);
201+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, expr.span, note_msg);
202+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, expr.span, note_msg);
203+ /// ```
204+ pub COLLAPSIBLE_SPAN_LINT_CALLS ,
205+ internal,
206+ "found collapsible `span_lint_and_then` calls"
207+ }
208+
145209declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
146210
147211impl EarlyLintPass for ClippyLintsInternal {
@@ -165,7 +229,7 @@ impl EarlyLintPass for ClippyLintsInternal {
165229 CLIPPY_LINTS_INTERNAL ,
166230 item. span ,
167231 "this constant should be before the previous constant due to lexical \
168- ordering",
232+ ordering",
169233 ) ;
170234 }
171235 }
@@ -195,8 +259,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
195259 if let ExprKind :: AddrOf ( _, _, ref inner_exp) = expr. kind;
196260 if let ExprKind :: Struct ( _, ref fields, _) = inner_exp. kind;
197261 let field = fields. iter( )
198- . find( |f| f. ident. as_str( ) == "desc" )
199- . expect( "lints must have a description field" ) ;
262+ . find( |f| f. ident. as_str( ) == "desc" )
263+ . expect( "lints must have a description field" ) ;
200264 if let ExprKind :: Lit ( Spanned {
201265 node: LitKind :: Str ( ref sym, _) ,
202266 ..
@@ -332,7 +396,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
332396 if let Some ( sugg) = self . map. get( & * fn_name. as_str( ) ) ;
333397 let ty = walk_ptrs_ty( cx. tables. expr_ty( & args[ 0 ] ) ) ;
334398 if match_type( cx, ty, & paths:: EARLY_CONTEXT )
335- || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
399+ || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
336400 then {
337401 span_lint_and_help(
338402 cx,
@@ -391,3 +455,155 @@ fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
391455 FnKind :: Closure ( ..) => false ,
392456 }
393457}
458+
459+ declare_lint_pass ! ( CollapsibleCalls => [ COLLAPSIBLE_SPAN_LINT_CALLS ] ) ;
460+
461+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for CollapsibleCalls {
462+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
463+ if_chain ! {
464+ if let ExprKind :: Call ( ref func, ref and_then_args) = expr. kind;
465+ if let ExprKind :: Path ( ref path) = func. kind;
466+ if match_qpath( path, & [ "span_lint_and_then" ] ) ;
467+ if and_then_args. len( ) == 5 ;
468+ if let ExprKind :: Closure ( _, _, body_id, _, _) = & and_then_args[ 4 ] . kind;
469+ let body = cx. tcx. hir( ) . body( * body_id) ;
470+ if let ExprKind :: Block ( block, _) = & body. value. kind;
471+ let stmts = & block. stmts;
472+ if stmts. len( ) == 1 && block. expr. is_none( ) ;
473+ if let StmtKind :: Semi ( only_expr) = & stmts[ 0 ] . kind;
474+ if let ExprKind :: MethodCall ( ref ps, _, ref span_call_args) = & only_expr. kind;
475+ let and_then_snippets = get_and_then_snippets( cx, and_then_args) ;
476+ let mut sle = SpanlessEq :: new( cx) . ignore_fn( ) ;
477+ then {
478+ match & * ps. ident. as_str( ) {
479+ "span_suggestion" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
480+ suggest_suggestion( cx, expr, & and_then_snippets, & span_suggestion_snippets( cx, span_call_args) ) ;
481+ } ,
482+ "span_help" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
483+ let help_snippet = snippet( cx, span_call_args[ 2 ] . span, r#""...""# ) ;
484+ suggest_help( cx, expr, & and_then_snippets, help_snippet. borrow( ) ) ;
485+ } ,
486+ "span_note" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
487+ let note_snippet = snippet( cx, span_call_args[ 2 ] . span, r#""...""# ) ;
488+ suggest_note( cx, expr, & and_then_snippets, note_snippet. borrow( ) ) ;
489+ } ,
490+ "help" => {
491+ let help_snippet = snippet( cx, span_call_args[ 1 ] . span, r#""...""# ) ;
492+ suggest_help( cx, expr, & and_then_snippets, help_snippet. borrow( ) ) ;
493+ }
494+ "note" => {
495+ let note_snippet = snippet( cx, span_call_args[ 1 ] . span, r#""...""# ) ;
496+ suggest_note( cx, expr, & and_then_snippets, note_snippet. borrow( ) ) ;
497+ }
498+ _ => ( ) ,
499+ }
500+ }
501+ }
502+ }
503+ }
504+
505+ struct AndThenSnippets < ' a > {
506+ cx : Cow < ' a , str > ,
507+ lint : Cow < ' a , str > ,
508+ span : Cow < ' a , str > ,
509+ msg : Cow < ' a , str > ,
510+ }
511+
512+ fn get_and_then_snippets < ' a , ' hir > (
513+ cx : & LateContext < ' _ , ' _ > ,
514+ and_then_snippets : & ' hir [ Expr < ' hir > ] ,
515+ ) -> AndThenSnippets < ' a > {
516+ let cx_snippet = snippet ( cx, and_then_snippets[ 0 ] . span , "cx" ) ;
517+ let lint_snippet = snippet ( cx, and_then_snippets[ 1 ] . span , ".." ) ;
518+ let span_snippet = snippet ( cx, and_then_snippets[ 2 ] . span , "span" ) ;
519+ let msg_snippet = snippet ( cx, and_then_snippets[ 3 ] . span , r#""...""# ) ;
520+
521+ AndThenSnippets {
522+ cx : cx_snippet,
523+ lint : lint_snippet,
524+ span : span_snippet,
525+ msg : msg_snippet,
526+ }
527+ }
528+
529+ struct SpanSuggestionSnippets < ' a > {
530+ help : Cow < ' a , str > ,
531+ sugg : Cow < ' a , str > ,
532+ applicability : Cow < ' a , str > ,
533+ }
534+
535+ fn span_suggestion_snippets < ' a , ' hir > (
536+ cx : & LateContext < ' _ , ' _ > ,
537+ span_call_args : & ' hir [ Expr < ' hir > ] ,
538+ ) -> SpanSuggestionSnippets < ' a > {
539+ let help_snippet = snippet ( cx, span_call_args[ 2 ] . span , r#""...""# ) ;
540+ let sugg_snippet = snippet ( cx, span_call_args[ 3 ] . span , ".." ) ;
541+ let applicability_snippet = snippet ( cx, span_call_args[ 4 ] . span , "Applicability::MachineApplicable" ) ;
542+
543+ SpanSuggestionSnippets {
544+ help : help_snippet,
545+ sugg : sugg_snippet,
546+ applicability : applicability_snippet,
547+ }
548+ }
549+
550+ fn suggest_suggestion (
551+ cx : & LateContext < ' _ , ' _ > ,
552+ expr : & Expr < ' _ > ,
553+ and_then_snippets : & AndThenSnippets < ' _ > ,
554+ span_suggestion_snippets : & SpanSuggestionSnippets < ' _ > ,
555+ ) {
556+ span_lint_and_sugg (
557+ cx,
558+ COLLAPSIBLE_SPAN_LINT_CALLS ,
559+ expr. span ,
560+ "this call is collapsible" ,
561+ "collapse into" ,
562+ format ! (
563+ "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})" ,
564+ and_then_snippets. cx,
565+ and_then_snippets. lint,
566+ and_then_snippets. span,
567+ and_then_snippets. msg,
568+ span_suggestion_snippets. help,
569+ span_suggestion_snippets. sugg,
570+ span_suggestion_snippets. applicability
571+ ) ,
572+ Applicability :: MachineApplicable ,
573+ ) ;
574+ }
575+
576+ fn suggest_help ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , and_then_snippets : & AndThenSnippets < ' _ > , help : & str ) {
577+ span_lint_and_sugg (
578+ cx,
579+ COLLAPSIBLE_SPAN_LINT_CALLS ,
580+ expr. span ,
581+ "this call is collapsible" ,
582+ "collapse into" ,
583+ format ! (
584+ "span_lint_and_help({}, {}, {}, {}, {})" ,
585+ and_then_snippets. cx, and_then_snippets. lint, and_then_snippets. span, and_then_snippets. msg, help
586+ ) ,
587+ Applicability :: MachineApplicable ,
588+ ) ;
589+ }
590+
591+ fn suggest_note ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , and_then_snippets : & AndThenSnippets < ' _ > , note : & str ) {
592+ span_lint_and_sugg (
593+ cx,
594+ COLLAPSIBLE_SPAN_LINT_CALLS ,
595+ expr. span ,
596+ "this call is collspible" ,
597+ "collapse into" ,
598+ format ! (
599+ "span_lint_and_note({}, {}, {}, {}, {}, {})" ,
600+ and_then_snippets. cx,
601+ and_then_snippets. lint,
602+ and_then_snippets. span,
603+ and_then_snippets. msg,
604+ and_then_snippets. span,
605+ note
606+ ) ,
607+ Applicability :: MachineApplicable ,
608+ ) ;
609+ }
0 commit comments