1
+ use crate :: utils:: SpanlessEq ;
1
2
use 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,
4
5
} ;
5
6
use if_chain:: if_chain;
6
7
use rustc_ast:: ast:: { Crate as AstCrate , ItemKind , LitKind , Name , NodeId } ;
@@ -10,13 +11,15 @@ use rustc_errors::Applicability;
10
11
use rustc_hir as hir;
11
12
use rustc_hir:: def:: { DefKind , Res } ;
12
13
use 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 } ;
14
15
use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
15
16
use rustc_middle:: hir:: map:: Map ;
16
17
use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
17
18
use rustc_span:: source_map:: { Span , Spanned } ;
18
19
use rustc_span:: symbol:: SymbolStr ;
19
20
21
+ use std:: borrow:: { Borrow , Cow } ;
22
+
20
23
declare_clippy_lint ! {
21
24
/// **What it does:** Checks for various things we like to keep tidy in clippy.
22
25
///
@@ -142,6 +145,67 @@ declare_clippy_lint! {
142
145
"found 'default lint description' in a lint declaration"
143
146
}
144
147
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
+
145
209
declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
146
210
147
211
impl EarlyLintPass for ClippyLintsInternal {
@@ -165,7 +229,7 @@ impl EarlyLintPass for ClippyLintsInternal {
165
229
CLIPPY_LINTS_INTERNAL ,
166
230
item. span ,
167
231
"this constant should be before the previous constant due to lexical \
168
- ordering",
232
+ ordering",
169
233
) ;
170
234
}
171
235
}
@@ -195,8 +259,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
195
259
if let ExprKind :: AddrOf ( _, _, ref inner_exp) = expr. kind;
196
260
if let ExprKind :: Struct ( _, ref fields, _) = inner_exp. kind;
197
261
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" ) ;
200
264
if let ExprKind :: Lit ( Spanned {
201
265
node: LitKind :: Str ( ref sym, _) ,
202
266
..
@@ -332,7 +396,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
332
396
if let Some ( sugg) = self . map. get( & * fn_name. as_str( ) ) ;
333
397
let ty = walk_ptrs_ty( cx. tables. expr_ty( & args[ 0 ] ) ) ;
334
398
if match_type( cx, ty, & paths:: EARLY_CONTEXT )
335
- || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
399
+ || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
336
400
then {
337
401
span_lint_and_help(
338
402
cx,
@@ -391,3 +455,155 @@ fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
391
455
FnKind :: Closure ( ..) => false ,
392
456
}
393
457
}
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