@@ -7,9 +7,10 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
7
7
use rustc_hir:: * ;
8
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
9
use rustc_middle:: hir:: map:: Map ;
10
- use rustc_middle:: ty:: TyCtxt ;
11
10
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
11
12
+ use std:: marker:: PhantomData ;
13
+
13
14
declare_clippy_lint ! {
14
15
/// **What it does:**
15
16
/// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
@@ -28,6 +29,11 @@ declare_clippy_lint! {
28
29
/// an expensive function, then it would be more efficient to use
29
30
/// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
30
31
///
32
+ /// Also, this lint uses a deliberately conservative metric for checking
33
+ /// if the inside of either body contains breaks or continues which will
34
+ /// cause it to not suggest a fix if either block contains a loop with
35
+ /// continues or breaks contained within the loop.
36
+ ///
31
37
/// **Example:**
32
38
///
33
39
/// ```rust
@@ -86,25 +92,21 @@ struct OptionIfLetElseOccurence {
86
92
}
87
93
88
94
struct ReturnBreakContinueVisitor < ' tcx > {
89
- tcx : TyCtxt < ' tcx > ,
90
95
seen_return_break_continue : bool ,
96
+ phantom_data : PhantomData < & ' tcx bool > ,
91
97
}
92
98
impl < ' tcx > ReturnBreakContinueVisitor < ' tcx > {
93
- fn new ( tcx : TyCtxt < ' tcx > ) -> ReturnBreakContinueVisitor < ' tcx > {
99
+ fn new ( ) -> ReturnBreakContinueVisitor < ' tcx > {
94
100
ReturnBreakContinueVisitor {
95
101
seen_return_break_continue : false ,
96
- tcx ,
102
+ phantom_data : PhantomData ,
97
103
}
98
104
}
99
-
100
- fn seen_return_break_continue ( & self ) -> bool {
101
- self . seen_return_break_continue
102
- }
103
105
}
104
106
impl < ' tcx > Visitor < ' tcx > for ReturnBreakContinueVisitor < ' tcx > {
105
107
type Map = Map < ' tcx > ;
106
108
fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
107
- NestedVisitorMap :: OnlyBodies ( self . tcx . hir ( ) )
109
+ NestedVisitorMap :: None
108
110
}
109
111
110
112
fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
@@ -116,22 +118,20 @@ impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> {
116
118
ExprKind :: Ret ( ..) | ExprKind :: Break ( ..) | ExprKind :: Continue ( ..) => {
117
119
self . seen_return_break_continue = true ;
118
120
} ,
119
- // Do nothing if encountering a loop, because internal breaks and
120
- // continues shouldn't be flagged
121
- ExprKind :: Loop ( .. ) => { } ,
121
+ // Something special could be done here to handle while or for loop
122
+ // desugaring, as this will detect a break if there's a while loop
123
+ // or a for loop inside the expression.
122
124
_ => {
123
125
rustc_hir:: intravisit:: walk_expr ( self , ex) ;
124
126
} ,
125
127
}
126
128
}
127
129
}
128
130
129
- fn contains_return_break_continue < ' tcx > ( cx : & LateContext < ' _ , ' tcx > , statements : & ' tcx [ Stmt < ' tcx > ] ) -> bool {
130
- let mut recursive_visitor: ReturnBreakContinueVisitor < ' tcx > = ReturnBreakContinueVisitor :: new ( cx. tcx ) ;
131
- for statement in statements {
132
- recursive_visitor. visit_stmt ( statement) ;
133
- }
134
- recursive_visitor. seen_return_break_continue ( )
131
+ fn contains_return_break_continue < ' tcx > ( expression : & ' tcx Expr < ' tcx > ) -> bool {
132
+ let mut recursive_visitor: ReturnBreakContinueVisitor < ' tcx > = ReturnBreakContinueVisitor :: new ( ) ;
133
+ recursive_visitor. visit_expr ( expression) ;
134
+ recursive_visitor. seen_return_break_continue
135
135
}
136
136
137
137
/// If this expression is the option if let/else construct we're detecting, then
@@ -146,16 +146,14 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
146
146
if !is_result_ok( cx, let_body) ; // Don't lint on Result::ok because a different lint does it already
147
147
if let PatKind :: TupleStruct ( _, & [ inner_pat] , _) = & arms[ 0 ] . pat. kind;
148
148
if let PatKind :: Binding ( _, _, id, _) = & inner_pat. kind;
149
+ if !contains_return_break_continue( arms[ 0 ] . body) ;
150
+ if !contains_return_break_continue( arms[ 1 ] . body) ;
149
151
then {
150
152
let some_body = if let ExprKind :: Block ( Block { stmts: statements, expr: Some ( expr) , .. } , _)
151
153
= & arms[ 0 ] . body. kind {
152
154
if let & [ ] = & statements {
153
155
expr
154
156
} else {
155
- // Don't trigger if there is a return, break, or continue inside
156
- if contains_return_break_continue( cx, statements) {
157
- return None ;
158
- }
159
157
& arms[ 0 ] . body
160
158
}
161
159
} else {
@@ -166,9 +164,6 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
166
164
if let & [ ] = & statements {
167
165
( expr, "map_or" )
168
166
} else {
169
- if contains_return_break_continue( cx, statements) {
170
- return None ;
171
- }
172
167
( & arms[ 1 ] . body, "map_or_else" )
173
168
}
174
169
} else {
@@ -179,7 +174,7 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
179
174
option: format!( "{}" , Sugg :: hir( cx, let_body, ".." ) ) ,
180
175
method_sugg: format!( "{}" , method_sugg) ,
181
176
some_expr: format!( "|{}| {}" , capture_name, Sugg :: hir( cx, some_body, ".." ) ) ,
182
- none_expr: format!( "{}{}" , if method_sugg == "map_or" { "" } else { "||" } , Sugg :: hir( cx, none_body, ".." ) )
177
+ none_expr: format!( "{}{}" , if method_sugg == "map_or" { "" } else { "|| " } , Sugg :: hir( cx, none_body, ".." ) )
183
178
} )
184
179
} else {
185
180
None
0 commit comments