1
1
use itertools:: Itertools ;
2
2
use rustc_hir:: {
3
- def:: Res , Arm , BinOpKind , Block , BlockCheckMode , Expr , ExprKind , Guard , HirId , PatKind , QPath , StmtKind ,
3
+ def:: Res , Arm , BinOpKind , Block , BlockCheckMode , Expr , ExprKind , Guard , HirId , PatKind , PathSegment , PrimTy , QPath ,
4
+ StmtKind ,
4
5
} ;
5
6
use rustc_lint:: { LateContext , LateLintPass } ;
6
7
use rustc_middle:: ty:: Ty ;
@@ -10,8 +11,8 @@ use rustc_span::{symbol::sym, Span};
10
11
use std:: ops:: Deref ;
11
12
12
13
use clippy_utils:: {
13
- consts:: constant, def_path_res , diagnostics:: span_lint_and_then, eq_expr_value, get_trait_def_id, higher:: If ,
14
- is_trait_method, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks_with_stmt, sugg:: Sugg ,
14
+ consts:: constant, diagnostics:: span_lint_and_then, eq_expr_value, get_trait_def_id, higher:: If , is_diag_trait_item ,
15
+ is_trait_method, meets_msrv, msrvs, path_res , path_to_local_id, paths, peel_blocks_with_stmt, sugg:: Sugg ,
15
16
ty:: implements_trait,
16
17
} ;
17
18
use rustc_errors:: Applicability ;
@@ -278,101 +279,97 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
278
279
/// # ;
279
280
/// ```
280
281
fn is_call_max_min_pattern < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < ClampSuggestion < ' tcx > > {
281
- fn for_methods < ' tcx > (
282
+ fn segment < ' tcx > ( cx : & LateContext < ' _ > , func : & Expr < ' tcx > ) -> Option < FunctionType < ' tcx > > {
283
+ match func. kind {
284
+ ExprKind :: Path ( QPath :: Resolved ( None , path) ) => {
285
+ let id = path. res . opt_def_id ( ) ?;
286
+ match cx. tcx . get_diagnostic_name ( id) {
287
+ Some ( sym:: cmp_min) => Some ( FunctionType :: CmpMin ) ,
288
+ Some ( sym:: cmp_max) => Some ( FunctionType :: CmpMax ) ,
289
+ _ if is_diag_trait_item ( cx, id, sym:: Ord ) => {
290
+ Some ( FunctionType :: OrdOrFloat ( path. segments . last ( ) . expect ( "infallible" ) ) )
291
+ } ,
292
+ _ => None ,
293
+ }
294
+ } ,
295
+ ExprKind :: Path ( QPath :: TypeRelative ( ty, seg) ) => {
296
+ matches ! ( path_res( cx, ty) , Res :: PrimTy ( PrimTy :: Float ( _) ) ) . then ( || FunctionType :: OrdOrFloat ( seg) )
297
+ } ,
298
+ _ => None ,
299
+ }
300
+ }
301
+
302
+ enum FunctionType < ' tcx > {
303
+ CmpMin ,
304
+ CmpMax ,
305
+ OrdOrFloat ( & ' tcx PathSegment < ' tcx > ) ,
306
+ }
307
+
308
+ fn extract_call_data ( e : ExprRef < ' _ > , is_first : bool ) -> Option < ( ExprRef < ' _ > , ExprRef < ' _ > , ExprRef < ' _ > , bool ) > {
309
+ if let ExprKind :: Call ( inner_fn, & [ ref third, ref fourth] ) = & e. kind {
310
+ Some ( ( inner_fn, third, fourth, is_first) )
311
+ } else {
312
+ None
313
+ }
314
+ }
315
+
316
+ fn check < ' tcx > (
282
317
cx : & LateContext < ' tcx > ,
283
- expr : & ' tcx Expr < ' tcx > ,
284
- max_is_first : bool ,
285
- first_res : Res ,
286
- second_res : Res ,
287
- is_float : bool ,
318
+ outer_fn : ExprRef < ' tcx > ,
319
+ first : ExprRef < ' tcx > ,
320
+ second : ExprRef < ' tcx > ,
321
+ span : Span ,
288
322
) -> Option < ClampSuggestion < ' tcx > > {
289
- let mut min = None ;
290
- let mut max = None ;
291
- let mut input = None ;
292
- let ( mut_one, mut_two) = if max_is_first {
293
- ( & mut min, & mut max)
294
- } else {
295
- ( & mut max, & mut min)
296
- } ;
297
- let mut process_consts = |arg_third, arg_fourth| {
298
- let third_is_const = constant ( cx, cx. typeck_results ( ) , simplify_kind ( arg_third) ) . is_some ( ) ;
299
- let fourth_is_const = constant ( cx, cx. typeck_results ( ) , simplify_kind ( arg_fourth) ) . is_some ( ) ;
323
+ if let Ok ( ( inner_fn, third, fourth, is_first) ) =
324
+ extract_call_data ( first, true ) . into_iter ( ) . chain ( extract_call_data ( second, false ) ) . exactly_one ( )
325
+ && let Some ( inner_seg) = segment ( cx, inner_fn)
326
+ && let Some ( outer_seg) = segment ( cx, outer_fn) {
327
+ let third_is_const = constant ( cx, cx. typeck_results ( ) , simplify_kind ( third) ) . is_some ( ) ;
328
+ let fourth_is_const = constant ( cx, cx. typeck_results ( ) , simplify_kind ( fourth) ) . is_some ( ) ;
329
+ let input;
330
+ let new_arg;
300
331
match ( third_is_const, fourth_is_const) {
301
- ( true , false ) => ( input, * mut_two ) = ( Some ( arg_fourth ) , Some ( arg_third ) ) ,
302
- ( false , true ) => ( input, * mut_two ) = ( Some ( arg_third ) , Some ( arg_fourth ) ) ,
303
- _ => { } ,
332
+ ( true , false ) => ( input, new_arg ) = ( fourth , third ) ,
333
+ ( false , true ) => ( input, new_arg ) = ( third , fourth ) ,
334
+ _ => return None ,
304
335
}
305
- } ;
306
- if let ExprKind :: Call (
307
- Expr { kind : ExprKind :: Path ( path) , hir_id, .. } , & [ ref arg_first, ref arg_second]
308
- ) = & expr. kind
309
- && cx. qpath_res ( path, * hir_id) == first_res {
310
- if let ExprKind :: Call (
311
- Expr { kind : ExprKind :: Path ( path_first) , hir_id, .. } , & [ ref arg_third, ref arg_fourth]
312
- ) = & simplify_kind ( arg_first) . kind
313
- && cx. qpath_res ( path_first, * hir_id) == second_res {
314
- * mut_one = Some ( arg_second) ;
315
- process_consts ( arg_third, arg_fourth) ;
316
- } else if let ExprKind :: Call (
317
- Expr { kind : ExprKind :: Path ( path_second) , hir_id, .. } , & [ ref arg_third, ref arg_fourth]
318
- ) = & simplify_kind ( arg_second) . kind
319
- && cx. qpath_res ( path_second, * hir_id) == second_res {
320
- * mut_one = Some ( arg_first) ;
321
- process_consts ( arg_third, arg_fourth) ;
336
+ let other_new_arg = if is_first {
337
+ second
338
+ } else {
339
+ first
340
+ } ;
341
+ let is_float = cx. typeck_results ( ) . expr_ty_adjusted ( input) . is_floating_point ( ) ;
342
+ let min_max_suggestion = Some ( ClampSuggestion {
343
+ params : InputMinMax { input, min : other_new_arg, max : new_arg, is_float } ,
344
+ span,
345
+ make_assignment : None ,
346
+ } ) ;
347
+ let max_min_suggestion = Some ( ClampSuggestion {
348
+ params : InputMinMax { input, min : new_arg, max : other_new_arg, is_float } ,
349
+ span,
350
+ make_assignment : None ,
351
+ } ) ;
352
+ match ( inner_seg, outer_seg) {
353
+ ( FunctionType :: CmpMin , FunctionType :: CmpMax ) => min_max_suggestion,
354
+ ( FunctionType :: CmpMax , FunctionType :: CmpMin ) => max_min_suggestion,
355
+ ( FunctionType :: OrdOrFloat ( first_segment) , FunctionType :: OrdOrFloat ( second_segment) ) => {
356
+ match ( first_segment. ident . as_str ( ) , second_segment. ident . as_str ( ) ) {
357
+ ( "min" , "max" ) => min_max_suggestion,
358
+ ( "max" , "min" ) => max_min_suggestion,
359
+ _ => None ,
360
+ }
361
+ }
362
+ _ => None ,
322
363
}
364
+ } else {
365
+ None
323
366
}
324
- let input = input?;
325
- let min = min?;
326
- let max = max?;
327
- Some ( ClampSuggestion {
328
- params : InputMinMax {
329
- input,
330
- min,
331
- max,
332
- is_float,
333
- } ,
334
- span : expr. span ,
335
- make_assignment : None ,
336
- } )
337
367
}
338
- // Lots of expensive queries coming up which we don't want to repeat for every expr, so short
339
- // circuit this a bit.
340
- let is_dual_call = |e| {
341
- matches ! (
342
- & simplify_kind( e) . kind,
343
- ExprKind :: Call (
344
- Expr {
345
- kind: ExprKind :: Path ( _) ,
346
- ..
347
- } ,
348
- & [ _, _]
349
- )
350
- )
351
- } ;
352
- let type_clampability = if let ExprKind :: Call (
353
- Expr {
354
- kind : ExprKind :: Path ( _) ,
355
- ..
356
- } ,
357
- & [ ref arg_first, ref arg_second] ,
358
- ) = & expr. kind
359
- && ( is_dual_call ( arg_first) ^ is_dual_call ( arg_second) ) {
360
- TypeClampability :: is_clampable ( cx, cx. typeck_results ( ) . expr_ty ( arg_first) )
368
+
369
+ if let ExprKind :: Call ( outer_fn, [ first, second] ) = & expr. kind {
370
+ check ( cx, outer_fn, first, second, expr. span ) . or_else ( || check ( cx, outer_fn, second, first, expr. span ) )
361
371
} else {
362
372
None
363
- } ?;
364
- let is_float = type_clampability. is_float ( ) ;
365
- let for_each_combo_of = |min, max| {
366
- for_methods ( cx, expr, true , max, min, is_float) . or_else ( || for_methods ( cx, expr, false , min, max, is_float) )
367
- } ;
368
- if is_float {
369
- let ( f32_min_res, f32_max_res) = for_min_max ( |m| def_path_res ( cx, & [ "f32" , m] ) ) ;
370
- let ( f64_min_res, f64_max_res) = for_min_max ( |m| def_path_res ( cx, & [ "f64" , m] ) ) ;
371
- for_each_combo_of ( f32_min_res, f32_max_res) . or_else ( || for_each_combo_of ( f64_min_res, f64_max_res) )
372
- } else {
373
- let ( core_min_res, core_max_res) = for_min_max ( |m| def_path_res ( cx, & [ "core" , "cmp" , m] ) ) ;
374
- let ( core_ord_min_res, core_ord_max_res) = for_min_max ( |m| def_path_res ( cx, & [ "core" , "cmp" , "Ord" , m] ) ) ;
375
- for_each_combo_of ( core_min_res, core_max_res) . or_else ( || for_each_combo_of ( core_ord_min_res, core_ord_max_res) )
376
373
}
377
374
}
378
375
@@ -720,10 +717,6 @@ fn ord_op_same_direction(a: BinOpKind, b: BinOpKind) -> bool {
720
717
}
721
718
}
722
719
723
- fn for_min_max < T > ( mut f : impl FnMut ( & str ) -> T ) -> ( T , T ) {
724
- ( f ( "min" ) , f ( "max" ) )
725
- }
726
-
727
720
/// Really similar to Cow, but doesn't have a `Clone` requirement.
728
721
#[ derive( Debug ) ]
729
722
enum MaybeBorrowedStmtKind < ' a > {
0 commit comments