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 as other_snippet , span_lint ,
4
+ span_lint_and_help , 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,65 @@ 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:** When `span_lint_and_then` function call and its
150
+ /// closure have one statment that is a method call of `span_suggestion`,
151
+ /// `span_help`, or `span_note` are using the same span argument,
152
+ /// or when closure have one statment that is a function call of `note` or
153
+ /// `help`, they should be replaced with one of three function calls which is
154
+ /// `span_lint_and_sugg`, span_lint_and_help` or `span_lint_and_note`.
155
+ ///
156
+ /// **Why is this bad?** There are wrapper `span_lint_and_*` functions, it is
157
+ /// convenient, more readable and more error resistant to use wrapper functions.
158
+ ///
159
+ /// **Known problems:** None
160
+ ///
161
+ /// *Example:**
162
+ /// Bad:
163
+ /// ```rust,ignore
164
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
165
+ /// db.span_suggestion(
166
+ /// expr.span,
167
+ /// help_msg,
168
+ /// sugg.to_string(),
169
+ /// Applicability::MachineApplicable,
170
+ /// );
171
+ /// });
172
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
173
+ /// db.span_help(expr.span, help_msg);
174
+ /// });
175
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
176
+ /// db.help(help_msg);
177
+ /// });
178
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
179
+ /// db.span_note(expr.span, note_msg);
180
+ /// });
181
+ /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
182
+ /// db.note(note_msg);
183
+ /// });
184
+ /// ```
185
+ ///
186
+ /// Good:
187
+ /// ```rust,ignore
188
+ /// span_lint_and_sugg(
189
+ /// cx,
190
+ /// TEST_LINT,
191
+ /// expr.span,
192
+ /// lint_msg,
193
+ /// help_msg,
194
+ /// sugg.to_string(),
195
+ /// Applicability::MachineApplicable,
196
+ /// );
197
+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, help_msg);
198
+ /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, help_msg);
199
+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, expr.span, note_msg);
200
+ /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, expr.span, note_msg);
201
+ /// ```
202
+ pub COLLAPSIBLE_SPAN_LINT_CALLS ,
203
+ internal,
204
+ "found collapsible `span_lint_and_then` calls"
205
+ }
206
+
145
207
declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
146
208
147
209
impl EarlyLintPass for ClippyLintsInternal {
@@ -165,7 +227,7 @@ impl EarlyLintPass for ClippyLintsInternal {
165
227
CLIPPY_LINTS_INTERNAL ,
166
228
item. span ,
167
229
"this constant should be before the previous constant due to lexical \
168
- ordering",
230
+ ordering",
169
231
) ;
170
232
}
171
233
}
@@ -195,8 +257,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
195
257
if let ExprKind :: AddrOf ( _, _, ref inner_exp) = expr. kind;
196
258
if let ExprKind :: Struct ( _, ref fields, _) = inner_exp. kind;
197
259
let field = fields. iter( )
198
- . find( |f| f. ident. as_str( ) == "desc" )
199
- . expect( "lints must have a description field" ) ;
260
+ . find( |f| f. ident. as_str( ) == "desc" )
261
+ . expect( "lints must have a description field" ) ;
200
262
if let ExprKind :: Lit ( Spanned {
201
263
node: LitKind :: Str ( ref sym, _) ,
202
264
..
@@ -332,7 +394,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
332
394
if let Some ( sugg) = self . map. get( & * fn_name. as_str( ) ) ;
333
395
let ty = walk_ptrs_ty( cx. tables. expr_ty( & args[ 0 ] ) ) ;
334
396
if match_type( cx, ty, & paths:: EARLY_CONTEXT )
335
- || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
397
+ || match_type( cx, ty, & paths:: LATE_CONTEXT ) ;
336
398
then {
337
399
span_lint_and_help(
338
400
cx,
@@ -391,3 +453,159 @@ fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
391
453
FnKind :: Closure ( ..) => false ,
392
454
}
393
455
}
456
+
457
+ declare_lint_pass ! ( CollapsibleCalls => [ COLLAPSIBLE_SPAN_LINT_CALLS ] ) ;
458
+
459
+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for CollapsibleCalls {
460
+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
461
+ if_chain ! {
462
+ if let ExprKind :: Call ( ref func, ref and_then_args) = expr. kind;
463
+ if let ExprKind :: Path ( ref path) = func. kind;
464
+ if match_qpath( path, & [ "span_lint_and_then" ] ) ;
465
+ if and_then_args. len( ) == 5 ;
466
+ if let ExprKind :: Closure ( _, _, body_id, _, _) = & and_then_args[ 4 ] . kind;
467
+ if let body = cx. tcx. hir( ) . body( * body_id) ;
468
+ if let ExprKind :: Block ( block, _) = & body. value. kind;
469
+ let stmts = & block. stmts;
470
+ if stmts. len( ) == 1 && block. expr. is_none( ) ;
471
+ if let StmtKind :: Semi ( only_expr) = & stmts[ 0 ] . kind;
472
+ if let ExprKind :: MethodCall ( ref ps, _, ref span_call_args) = & only_expr. kind;
473
+ let and_then_snippets = get_and_then_snippets( cx, and_then_args) ;
474
+ let mut sle = SpanlessEq :: new( cx) . ignore_fn( ) ;
475
+ then {
476
+ match & * ps. ident. as_str( ) {
477
+ "span_suggestion" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
478
+ suggest_suggestion( cx, expr, & and_then_snippets, & span_suggestion_snippets( cx, span_call_args) ) ;
479
+ } ,
480
+ "span_help" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
481
+ let help_snippet = snippet( cx, span_call_args[ 2 ] . span) ;
482
+ suggest_help( cx, expr, & and_then_snippets, help_snippet. borrow( ) ) ;
483
+ } ,
484
+ "span_note" if sle. eq_expr( & and_then_args[ 2 ] , & span_call_args[ 1 ] ) => {
485
+ let note_snippet = snippet( cx, span_call_args[ 2 ] . span) ;
486
+ suggest_note( cx, expr, & and_then_snippets, note_snippet. borrow( ) ) ;
487
+ } ,
488
+ "help" => {
489
+ let help_snippet = snippet( cx, span_call_args[ 1 ] . span) ;
490
+ suggest_help( cx, expr, & and_then_snippets, help_snippet. borrow( ) ) ;
491
+ }
492
+ "note" => {
493
+ let note_snippet = snippet( cx, span_call_args[ 1 ] . span) ;
494
+ suggest_note( cx, expr, & and_then_snippets, note_snippet. borrow( ) ) ;
495
+ }
496
+ _ => ( ) ,
497
+ }
498
+ }
499
+ }
500
+ }
501
+ }
502
+
503
+ struct AndThenSnippets < ' a > {
504
+ cx : Cow < ' a , str > ,
505
+ lint : Cow < ' a , str > ,
506
+ span : Cow < ' a , str > ,
507
+ msg : Cow < ' a , str > ,
508
+ }
509
+
510
+ fn get_and_then_snippets < ' a , ' hir > (
511
+ cx : & LateContext < ' _ , ' _ > ,
512
+ and_then_snippets : & ' hir [ Expr < ' hir > ] ,
513
+ ) -> AndThenSnippets < ' a > {
514
+ let cx_snippet = snippet ( cx, and_then_snippets[ 0 ] . span ) ;
515
+ let lint_snippet = snippet ( cx, and_then_snippets[ 1 ] . span ) ;
516
+ let span_snippet = snippet ( cx, and_then_snippets[ 2 ] . span ) ;
517
+ let msg_snippet = snippet ( cx, and_then_snippets[ 3 ] . span ) ;
518
+
519
+ AndThenSnippets {
520
+ cx : cx_snippet,
521
+ lint : lint_snippet,
522
+ span : span_snippet,
523
+ msg : msg_snippet,
524
+ }
525
+ }
526
+
527
+ struct SpanSuggestionSnippets < ' a > {
528
+ help : Cow < ' a , str > ,
529
+ sugg : Cow < ' a , str > ,
530
+ applicability : Cow < ' a , str > ,
531
+ }
532
+
533
+ fn span_suggestion_snippets < ' a , ' hir > (
534
+ cx : & LateContext < ' _ , ' _ > ,
535
+ span_call_args : & ' hir [ Expr < ' hir > ] ,
536
+ ) -> SpanSuggestionSnippets < ' a > {
537
+ let help_snippet = snippet ( cx, span_call_args[ 2 ] . span ) ;
538
+ let sugg_snippet = snippet ( cx, span_call_args[ 3 ] . span ) ;
539
+ let applicability_snippet = snippet ( cx, span_call_args[ 4 ] . span ) ;
540
+
541
+ SpanSuggestionSnippets {
542
+ help : help_snippet,
543
+ sugg : sugg_snippet,
544
+ applicability : applicability_snippet,
545
+ }
546
+ }
547
+
548
+ fn suggest_suggestion (
549
+ cx : & LateContext < ' _ , ' _ > ,
550
+ expr : & Expr < ' _ > ,
551
+ and_then_snippets : & AndThenSnippets < ' _ > ,
552
+ span_suggestion_snippets : & SpanSuggestionSnippets < ' _ > ,
553
+ ) {
554
+ span_lint_and_sugg (
555
+ cx,
556
+ COLLAPSIBLE_SPAN_LINT_CALLS ,
557
+ expr. span ,
558
+ "this call is collapsible" ,
559
+ "collapse into" ,
560
+ format ! (
561
+ "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})" ,
562
+ and_then_snippets. cx,
563
+ and_then_snippets. lint,
564
+ and_then_snippets. span,
565
+ and_then_snippets. msg,
566
+ span_suggestion_snippets. help,
567
+ span_suggestion_snippets. sugg,
568
+ span_suggestion_snippets. applicability
569
+ ) ,
570
+ Applicability :: MachineApplicable ,
571
+ ) ;
572
+ }
573
+
574
+ fn suggest_help ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , and_then_snippets : & AndThenSnippets < ' _ > , help : & str ) {
575
+ span_lint_and_sugg (
576
+ cx,
577
+ COLLAPSIBLE_SPAN_LINT_CALLS ,
578
+ expr. span ,
579
+ "this call is collapsible" ,
580
+ "collapse into" ,
581
+ format ! (
582
+ "span_lint_and_help({}, {}, {}, {}, {})" ,
583
+ and_then_snippets. cx, and_then_snippets. lint, and_then_snippets. span, and_then_snippets. msg, help
584
+ ) ,
585
+ Applicability :: MachineApplicable ,
586
+ ) ;
587
+ }
588
+
589
+ fn suggest_note ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , and_then_snippets : & AndThenSnippets < ' _ > , note : & str ) {
590
+ span_lint_and_sugg (
591
+ cx,
592
+ COLLAPSIBLE_SPAN_LINT_CALLS ,
593
+ expr. span ,
594
+ "this call is collspible" ,
595
+ "collapse into" ,
596
+ format ! (
597
+ "span_lint_and_note({}, {}, {}, {}, {}, {})" ,
598
+ and_then_snippets. cx,
599
+ and_then_snippets. lint,
600
+ and_then_snippets. span,
601
+ and_then_snippets. msg,
602
+ and_then_snippets. span,
603
+ note
604
+ ) ,
605
+ Applicability :: MachineApplicable ,
606
+ ) ;
607
+ }
608
+
609
+ fn snippet < ' a > ( cx : & LateContext < ' _ , ' _ > , span : Span ) -> Cow < ' a , str > {
610
+ other_snippet ( cx, span, "Should not be this snippet" )
611
+ }
0 commit comments