@@ -7,9 +7,10 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
77use rustc_hir:: * ;
88use rustc_lint:: { LateContext , LateLintPass } ;
99use rustc_middle:: hir:: map:: Map ;
10- use rustc_middle:: ty:: TyCtxt ;
1110use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1211
12+ use std:: marker:: PhantomData ;
13+
1314declare_clippy_lint ! {
1415 /// **What it does:**
1516 /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
@@ -28,6 +29,11 @@ declare_clippy_lint! {
2829 /// an expensive function, then it would be more efficient to use
2930 /// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
3031 ///
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+ ///
3137 /// **Example:**
3238 ///
3339 /// ```rust
@@ -86,25 +92,21 @@ struct OptionIfLetElseOccurence {
8692}
8793
8894struct ReturnBreakContinueVisitor < ' tcx > {
89- tcx : TyCtxt < ' tcx > ,
9095 seen_return_break_continue : bool ,
96+ phantom_data : PhantomData < & ' tcx bool > ,
9197}
9298impl < ' tcx > ReturnBreakContinueVisitor < ' tcx > {
93- fn new ( tcx : TyCtxt < ' tcx > ) -> ReturnBreakContinueVisitor < ' tcx > {
99+ fn new ( ) -> ReturnBreakContinueVisitor < ' tcx > {
94100 ReturnBreakContinueVisitor {
95101 seen_return_break_continue : false ,
96- tcx ,
102+ phantom_data : PhantomData ,
97103 }
98104 }
99-
100- fn seen_return_break_continue ( & self ) -> bool {
101- self . seen_return_break_continue
102- }
103105}
104106impl < ' tcx > Visitor < ' tcx > for ReturnBreakContinueVisitor < ' tcx > {
105107 type Map = Map < ' tcx > ;
106108 fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
107- NestedVisitorMap :: OnlyBodies ( self . tcx . hir ( ) )
109+ NestedVisitorMap :: None
108110 }
109111
110112 fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
@@ -116,22 +118,20 @@ impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> {
116118 ExprKind :: Ret ( ..) | ExprKind :: Break ( ..) | ExprKind :: Continue ( ..) => {
117119 self . seen_return_break_continue = true ;
118120 } ,
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.
122124 _ => {
123125 rustc_hir:: intravisit:: walk_expr ( self , ex) ;
124126 } ,
125127 }
126128 }
127129}
128130
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
135135}
136136
137137/// 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>) -
146146 if !is_result_ok( cx, let_body) ; // Don't lint on Result::ok because a different lint does it already
147147 if let PatKind :: TupleStruct ( _, & [ inner_pat] , _) = & arms[ 0 ] . pat. kind;
148148 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) ;
149151 then {
150152 let some_body = if let ExprKind :: Block ( Block { stmts: statements, expr: Some ( expr) , .. } , _)
151153 = & arms[ 0 ] . body. kind {
152154 if let & [ ] = & statements {
153155 expr
154156 } 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- }
159157 & arms[ 0 ] . body
160158 }
161159 } else {
@@ -166,9 +164,6 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
166164 if let & [ ] = & statements {
167165 ( expr, "map_or" )
168166 } else {
169- if contains_return_break_continue( cx, statements) {
170- return None ;
171- }
172167 ( & arms[ 1 ] . body, "map_or_else" )
173168 }
174169 } else {
@@ -179,7 +174,7 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -
179174 option: format!( "{}" , Sugg :: hir( cx, let_body, ".." ) ) ,
180175 method_sugg: format!( "{}" , method_sugg) ,
181176 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, ".." ) )
183178 } )
184179 } else {
185180 None
0 commit comments