@@ -4,11 +4,12 @@ use clippy_utils::msrvs::{self, Msrv};
4
4
use clippy_utils:: peel_blocks;
5
5
use clippy_utils:: source:: snippet_with_context;
6
6
use clippy_utils:: ty:: is_type_diagnostic_item;
7
- use clippy_utils:: visitors:: { for_each_expr , Descend } ;
7
+ use clippy_utils:: visitors:: { Descend , Visitable } ;
8
8
use if_chain:: if_chain;
9
9
use rustc_data_structures:: fx:: FxHashSet ;
10
10
use rustc_errors:: Applicability ;
11
- use rustc_hir:: { Expr , ExprKind , MatchSource , Pat , PatKind , QPath , Stmt , StmtKind } ;
11
+ use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
12
+ use rustc_hir:: { Expr , ExprKind , HirId , ItemId , Local , MatchSource , Pat , PatKind , QPath , Stmt , StmtKind , Ty } ;
12
13
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
13
14
use rustc_middle:: lint:: in_external_macro;
14
15
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -162,61 +163,102 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat:
162
163
) ;
163
164
}
164
165
165
- fn expr_diverges ( cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) -> bool {
166
- fn is_never ( cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) -> bool {
167
- if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( expr) {
168
- return ty. is_never ( ) ;
169
- }
170
- false
166
+ /// Check whether an expression is divergent. May give false negatives.
167
+ fn expr_diverges ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
168
+ struct V < ' cx , ' tcx > {
169
+ cx : & ' cx LateContext < ' tcx > ,
170
+ res : ControlFlow < ( ) , Descend > ,
171
171
}
172
- // We can't just call is_never on expr and be done, because the type system
173
- // sometimes coerces the ! type to something different before we can get
174
- // our hands on it. So instead, we do a manual search. We do fall back to
175
- // is_never in some places when there is no better alternative.
176
- for_each_expr ( expr, |ex| {
177
- match ex. kind {
178
- ExprKind :: Continue ( _) | ExprKind :: Break ( _, _) | ExprKind :: Ret ( _) => ControlFlow :: Break ( ( ) ) ,
179
- ExprKind :: Call ( call, _) => {
180
- if is_never ( cx, ex) || is_never ( cx, call) {
181
- return ControlFlow :: Break ( ( ) ) ;
182
- }
183
- ControlFlow :: Continue ( Descend :: Yes )
184
- } ,
185
- ExprKind :: MethodCall ( ..) => {
186
- if is_never ( cx, ex) {
187
- return ControlFlow :: Break ( ( ) ) ;
188
- }
189
- ControlFlow :: Continue ( Descend :: Yes )
190
- } ,
191
- ExprKind :: If ( if_expr, if_then, if_else) => {
192
- let else_diverges = if_else. map_or ( false , |ex| expr_diverges ( cx, ex) ) ;
193
- let diverges = expr_diverges ( cx, if_expr) || ( else_diverges && expr_diverges ( cx, if_then) ) ;
194
- if diverges {
195
- return ControlFlow :: Break ( ( ) ) ;
196
- }
197
- ControlFlow :: Continue ( Descend :: No )
198
- } ,
199
- ExprKind :: Match ( match_expr, match_arms, _) => {
200
- let diverges = expr_diverges ( cx, match_expr)
201
- || match_arms. iter ( ) . all ( |arm| {
202
- let guard_diverges = arm. guard . as_ref ( ) . map_or ( false , |g| expr_diverges ( cx, g. body ( ) ) ) ;
203
- guard_diverges || expr_diverges ( cx, arm. body )
204
- } ) ;
205
- if diverges {
206
- return ControlFlow :: Break ( ( ) ) ;
172
+ impl < ' tcx > Visitor < ' tcx > for V < ' _ , ' _ > {
173
+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' tcx > ) {
174
+ fn is_never ( cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) -> bool {
175
+ if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( expr) {
176
+ return ty. is_never ( ) ;
207
177
}
208
- ControlFlow :: Continue ( Descend :: No )
209
- } ,
178
+ false
179
+ }
180
+
181
+ if self . res . is_break ( ) {
182
+ return ;
183
+ }
210
184
211
- // Don't continue into loops or labeled blocks, as they are breakable,
212
- // and we'd have to start checking labels.
213
- ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Loop ( ..) => ControlFlow :: Continue ( Descend :: No ) ,
185
+ // We can't just call is_never on expr and be done, because the type system
186
+ // sometimes coerces the ! type to something different before we can get
187
+ // our hands on it. So instead, we do a manual search. We do fall back to
188
+ // is_never in some places when there is no better alternative.
189
+ self . res = match e. kind {
190
+ ExprKind :: Continue ( _) | ExprKind :: Break ( _, _) | ExprKind :: Ret ( _) => ControlFlow :: Break ( ( ) ) ,
191
+ ExprKind :: Call ( call, _) => {
192
+ if is_never ( self . cx , e) || is_never ( self . cx , call) {
193
+ ControlFlow :: Break ( ( ) )
194
+ } else {
195
+ ControlFlow :: Continue ( Descend :: Yes )
196
+ }
197
+ } ,
198
+ ExprKind :: MethodCall ( ..) => {
199
+ if is_never ( self . cx , e) {
200
+ ControlFlow :: Break ( ( ) )
201
+ } else {
202
+ ControlFlow :: Continue ( Descend :: Yes )
203
+ }
204
+ } ,
205
+ ExprKind :: If ( if_expr, if_then, if_else) => {
206
+ let else_diverges = if_else. map_or ( false , |ex| expr_diverges ( self . cx , ex) ) ;
207
+ let diverges =
208
+ expr_diverges ( self . cx , if_expr) || ( else_diverges && expr_diverges ( self . cx , if_then) ) ;
209
+ if diverges {
210
+ ControlFlow :: Break ( ( ) )
211
+ } else {
212
+ ControlFlow :: Continue ( Descend :: No )
213
+ }
214
+ } ,
215
+ ExprKind :: Match ( match_expr, match_arms, _) => {
216
+ let diverges = expr_diverges ( self . cx , match_expr)
217
+ || match_arms. iter ( ) . all ( |arm| {
218
+ let guard_diverges = arm. guard . as_ref ( ) . map_or ( false , |g| expr_diverges ( self . cx , g. body ( ) ) ) ;
219
+ guard_diverges || expr_diverges ( self . cx , arm. body )
220
+ } ) ;
221
+ if diverges {
222
+ ControlFlow :: Break ( ( ) )
223
+ } else {
224
+ ControlFlow :: Continue ( Descend :: No )
225
+ }
226
+ } ,
214
227
215
- // Default: descend
216
- _ => ControlFlow :: Continue ( Descend :: Yes ) ,
228
+ // Don't continue into loops or labeled blocks, as they are breakable,
229
+ // and we'd have to start checking labels.
230
+ ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Loop ( ..) => ControlFlow :: Continue ( Descend :: No ) ,
231
+
232
+ // Default: descend
233
+ _ => ControlFlow :: Continue ( Descend :: Yes ) ,
234
+ } ;
235
+ if let ControlFlow :: Continue ( Descend :: Yes ) = self . res {
236
+ walk_expr ( self , e) ;
237
+ }
238
+ }
239
+
240
+ fn visit_local ( & mut self , local : & ' tcx Local < ' _ > ) {
241
+ // Don't visit the else block of a let/else statement as it will not make
242
+ // the statement divergent even though the else block is divergent.
243
+ if let Some ( init) = local. init {
244
+ self . visit_expr ( init) ;
245
+ }
217
246
}
218
- } )
219
- . is_some ( )
247
+
248
+ // Avoid unnecessary `walk_*` calls.
249
+ fn visit_ty ( & mut self , _: & ' tcx Ty < ' tcx > ) { }
250
+ fn visit_pat ( & mut self , _: & ' tcx Pat < ' tcx > ) { }
251
+ fn visit_qpath ( & mut self , _: & ' tcx QPath < ' tcx > , _: HirId , _: Span ) { }
252
+ // Avoid monomorphising all `visit_*` functions.
253
+ fn visit_nested_item ( & mut self , _: ItemId ) { }
254
+ }
255
+
256
+ let mut v = V {
257
+ cx,
258
+ res : ControlFlow :: Continue ( Descend :: Yes ) ,
259
+ } ;
260
+ expr. visit ( & mut v) ;
261
+ v. res . is_break ( )
220
262
}
221
263
222
264
fn pat_allowed_for_else ( cx : & LateContext < ' _ > , pat : & ' _ Pat < ' _ > , check_types : bool ) -> bool {
0 commit comments