1
+ use clippy_utils:: source:: snippet;
2
+ use rustc_middle:: lint:: in_external_macro;
1
3
use rustc_hir:: * ;
2
- use rustc_lint:: { LateContext , LateLintPass } ;
4
+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
3
5
use rustc_session:: declare_lint_pass;
6
+ use rustc_errors:: Applicability ;
7
+ use clippy_utils:: visitors:: { for_each_expr, Descend } ;
8
+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
9
+ use std:: ops:: ControlFlow ;
4
10
5
11
declare_clippy_lint ! {
6
12
/// ### What it does
7
- /// Checks for stacked `if` and `match`, e.g., `if if `.
13
+ /// Checks for `if if ` and `match match `.
8
14
///
9
15
/// ### Why is this bad?
10
- /// Stacked `if`'s and `match`'s are hard to read.
16
+ /// `if if` and `match match` are hard to read.
11
17
///
12
18
/// ### Example
13
19
/// ```no_run
@@ -17,23 +23,19 @@ declare_clippy_lint! {
17
23
/// e == f
18
24
/// } {
19
25
/// println!("true");
20
- /// } else {
21
- /// println!("false");
22
26
/// }
23
27
/// ```
24
28
///
25
29
/// Use instead:
26
30
/// ```no_run
27
- /// let cond = if a == b {
31
+ /// let result = if a == b {
28
32
/// c == d
29
33
/// } else {
30
34
/// e == f
31
35
/// };
32
36
///
33
- /// if cond {
37
+ /// if result {
34
38
/// println!("true");
35
- /// } else {
36
- /// println!("false");
37
39
/// }
38
40
/// ```
39
41
#[ clippy:: version = "1.82.0" ]
@@ -46,6 +48,48 @@ declare_lint_pass!(StackedIfMatch => [STACKED_IF_MATCH]);
46
48
47
49
impl < ' tcx > LateLintPass < ' tcx > for StackedIfMatch {
48
50
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
51
+ if expr. span . from_expansion ( ) || in_external_macro ( cx. sess ( ) , expr. span ) {
52
+ return ;
53
+ }
54
+
55
+ let Some ( ( cond, keyword) ) = ( match expr. kind {
56
+ ExprKind :: If ( if_expr, _, _) => Some ( ( if_expr, "if" ) ) ,
57
+ ExprKind :: Match ( match_expr, _, MatchSource :: Normal ) => Some ( ( match_expr, "match" ) ) ,
58
+ _ => None ,
59
+ } ) else {
60
+ return ;
61
+ } ;
62
+
63
+ let cond_snippet = snippet ( cx, cond. span , "" ) ;
64
+ if !cond_snippet. starts_with ( "if" ) && !cond_snippet. starts_with ( "match" ) {
65
+ return ;
66
+ }
67
+
68
+ for_each_expr ( cx, cond, |sub_expr| {
69
+ if matches ! ( sub_expr. kind, ExprKind :: DropTemps ( ..) ) {
70
+ return ControlFlow :: Continue ( Descend :: Yes ) ;
71
+ }
72
+
73
+ if !sub_expr. span . eq_ctxt ( expr. span ) || sub_expr. span . lo ( ) != cond. span . lo ( ) {
74
+ return ControlFlow :: Continue ( Descend :: No ) ;
75
+ }
49
76
77
+ if ( keyword == "if" && matches ! ( sub_expr. kind, ExprKind :: If ( ..) ) )
78
+ || ( keyword == "match" && matches ! ( sub_expr. kind, ExprKind :: Match ( .., MatchSource :: Normal ) ) ) {
79
+ let inner_snippet = snippet ( cx, sub_expr. span , ".." ) ;
80
+ span_lint_and_sugg (
81
+ cx,
82
+ STACKED_IF_MATCH ,
83
+ expr. span . with_hi ( sub_expr. span . hi ( ) ) ,
84
+ format ! ( "avoid using `{keyword} {keyword}`" ) ,
85
+ format ! ( "try binding inner `{keyword}` with `let`" ) ,
86
+ format ! ( "let result = {inner_snippet}; {keyword} result" ) ,
87
+ Applicability :: MachineApplicable ,
88
+ ) ;
89
+ ControlFlow :: Break ( ( ) )
90
+ } else {
91
+ ControlFlow :: Continue ( Descend :: Yes )
92
+ }
93
+ } ) ;
50
94
}
51
95
}
0 commit comments