1
+ use clippy_config:: Conf ;
2
+ use clippy_config:: msrvs:: { self , Msrv } ;
1
3
use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
2
4
use clippy_utils:: eq_expr_value;
3
5
use clippy_utils:: source:: SpanRangeExt ;
@@ -7,7 +9,7 @@ use rustc_errors::Applicability;
7
9
use rustc_hir:: intravisit:: { FnKind , Visitor , walk_expr} ;
8
10
use rustc_hir:: { BinOpKind , Body , Expr , ExprKind , FnDecl , UnOp } ;
9
11
use rustc_lint:: { LateContext , LateLintPass , Level } ;
10
- use rustc_session:: declare_lint_pass ;
12
+ use rustc_session:: { RustcVersion , impl_lint_pass } ;
11
13
use rustc_span:: def_id:: LocalDefId ;
12
14
use rustc_span:: { Span , sym} ;
13
15
@@ -69,9 +71,25 @@ declare_clippy_lint! {
69
71
}
70
72
71
73
// For each pairs, both orders are considered.
72
- const METHODS_WITH_NEGATION : [ ( & str , & str ) ; 2 ] = [ ( "is_some" , "is_none" ) , ( "is_err" , "is_ok" ) ] ;
74
+ const METHODS_WITH_NEGATION : [ ( Option < RustcVersion > , & str , & str ) ; 3 ] = [
75
+ ( None , "is_some" , "is_none" ) ,
76
+ ( None , "is_err" , "is_ok" ) ,
77
+ ( Some ( msrvs:: IS_NONE_OR ) , "is_some_and" , "is_none_or" ) ,
78
+ ] ;
79
+
80
+ pub struct NonminimalBool {
81
+ msrv : Msrv ,
82
+ }
83
+
84
+ impl NonminimalBool {
85
+ pub fn new ( conf : & ' static Conf ) -> Self {
86
+ Self {
87
+ msrv : conf. msrv . clone ( ) ,
88
+ }
89
+ }
90
+ }
73
91
74
- declare_lint_pass ! ( NonminimalBool => [ NONMINIMAL_BOOL , OVERLY_COMPLEX_BOOL_EXPR ] ) ;
92
+ impl_lint_pass ! ( NonminimalBool => [ NONMINIMAL_BOOL , OVERLY_COMPLEX_BOOL_EXPR ] ) ;
75
93
76
94
impl < ' tcx > LateLintPass < ' tcx > for NonminimalBool {
77
95
fn check_fn (
@@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
83
101
_: Span ,
84
102
_: LocalDefId ,
85
103
) {
86
- NonminimalBoolVisitor { cx } . visit_body ( body) ;
104
+ NonminimalBoolVisitor { cx, msrv : & self . msrv } . visit_body ( body) ;
87
105
}
88
106
89
107
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
@@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
100
118
_ => { } ,
101
119
}
102
120
}
121
+
122
+ extract_msrv_attr ! ( LateContext ) ;
103
123
}
104
124
105
125
fn inverted_bin_op_eq_str ( op : BinOpKind ) -> Option < & ' static str > {
@@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition(
176
196
) ;
177
197
}
178
198
179
- fn check_simplify_not ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
199
+ fn check_simplify_not ( cx : & LateContext < ' _ > , msrv : & Msrv , expr : & Expr < ' _ > ) {
180
200
if let ExprKind :: Unary ( UnOp :: Not , inner) = & expr. kind
181
201
&& !expr. span . from_expansion ( )
182
202
&& !inner. span . from_expansion ( )
183
- && let Some ( suggestion) = simplify_not ( cx, inner)
203
+ && let Some ( suggestion) = simplify_not ( cx, msrv , inner)
184
204
&& cx. tcx . lint_level_at_node ( NONMINIMAL_BOOL , expr. hir_id ) . 0 != Level :: Allow
185
205
{
186
206
span_lint_and_sugg (
@@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
197
217
198
218
struct NonminimalBoolVisitor < ' a , ' tcx > {
199
219
cx : & ' a LateContext < ' tcx > ,
220
+ msrv : & ' a Msrv ,
200
221
}
201
222
202
223
use quine_mc_cluskey:: Bool ;
@@ -205,7 +226,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
205
226
cx : & ' a LateContext < ' tcx > ,
206
227
}
207
228
208
- impl < ' a , ' tcx , ' v > Hir2Qmm < ' a , ' tcx , ' v > {
229
+ impl < ' v > Hir2Qmm < ' _ , ' _ , ' v > {
209
230
fn extract ( & mut self , op : BinOpKind , a : & [ & ' v Expr < ' _ > ] , mut v : Vec < Bool > ) -> Result < Vec < Bool > , String > {
210
231
for a in a {
211
232
if let ExprKind :: Binary ( binop, lhs, rhs) = & a. kind {
@@ -289,10 +310,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
289
310
struct SuggestContext < ' a , ' tcx , ' v > {
290
311
terminals : & ' v [ & ' v Expr < ' v > ] ,
291
312
cx : & ' a LateContext < ' tcx > ,
313
+ msrv : & ' a Msrv ,
292
314
output : String ,
293
315
}
294
316
295
- impl < ' a , ' tcx , ' v > SuggestContext < ' a , ' tcx , ' v > {
317
+ impl SuggestContext < ' _ , ' _ , ' _ > {
296
318
fn recurse ( & mut self , suggestion : & Bool ) -> Option < ( ) > {
297
319
use quine_mc_cluskey:: Bool :: { And , False , Not , Or , Term , True } ;
298
320
match suggestion {
@@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
311
333
} ,
312
334
Term ( n) => {
313
335
let terminal = self . terminals [ n as usize ] ;
314
- if let Some ( str) = simplify_not ( self . cx , terminal) {
336
+ if let Some ( str) = simplify_not ( self . cx , self . msrv , terminal) {
315
337
self . output . push_str ( & str) ;
316
338
} else {
317
339
self . output . push ( '!' ) ;
@@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
358
380
}
359
381
}
360
382
361
- fn simplify_not ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Option < String > {
383
+ fn simplify_not ( cx : & LateContext < ' _ > , curr_msrv : & Msrv , expr : & Expr < ' _ > ) -> Option < String > {
362
384
match & expr. kind {
363
385
ExprKind :: Binary ( binop, lhs, rhs) => {
364
386
if !implements_ord ( cx, lhs) {
@@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
389
411
Some ( format ! ( "{lhs_snippet}{op}{rhs_snippet}" ) )
390
412
} )
391
413
} ,
392
- ExprKind :: MethodCall ( path, receiver, [ ] , _) => {
414
+ ExprKind :: MethodCall ( path, receiver, args , _) => {
393
415
let type_of_receiver = cx. typeck_results ( ) . expr_ty ( receiver) ;
394
416
if !is_type_diagnostic_item ( cx, type_of_receiver, sym:: Option )
395
417
&& !is_type_diagnostic_item ( cx, type_of_receiver, sym:: Result )
@@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
399
421
METHODS_WITH_NEGATION
400
422
. iter ( )
401
423
. copied ( )
402
- . flat_map ( |( a, b) | vec ! [ ( a, b) , ( b, a) ] )
403
- . find ( |& ( a, _) | {
404
- let path: & str = path. ident . name . as_str ( ) ;
405
- a == path
424
+ . flat_map ( |( msrv, a, b) | vec ! [ ( msrv, a, b) , ( msrv, b, a) ] )
425
+ . find ( |& ( msrv, a, _) | msrv. is_none_or ( |msrv| curr_msrv. meets ( msrv) ) && a == path. ident . name . as_str ( ) )
426
+ . and_then ( |( _, _, neg_method) | {
427
+ let negated_args = args
428
+ . iter ( )
429
+ . map ( |arg| simplify_not ( cx, curr_msrv, arg) )
430
+ . collect :: < Option < Vec < _ > > > ( ) ?
431
+ . join ( ", " ) ;
432
+ Some ( format ! (
433
+ "{}.{neg_method}({negated_args})" ,
434
+ receiver. span. get_source_text( cx) ?
435
+ ) )
406
436
} )
407
- . and_then ( |( _, neg_method) | Some ( format ! ( "{}.{neg_method}()" , receiver. span. get_source_text( cx) ?) ) )
408
437
} ,
438
+ ExprKind :: Closure ( closure) => {
439
+ let body = cx. tcx . hir ( ) . body ( closure. body ) ;
440
+ let params = body
441
+ . params
442
+ . iter ( )
443
+ . map ( |param| param. span . get_source_text ( cx) . map ( |t| t. to_string ( ) ) )
444
+ . collect :: < Option < Vec < _ > > > ( ) ?
445
+ . join ( ", " ) ;
446
+ let negated = simplify_not ( cx, curr_msrv, body. value ) ?;
447
+ Some ( format ! ( "|{params}| {negated}" ) )
448
+ } ,
449
+ ExprKind :: Unary ( UnOp :: Not , expr) => expr. span . get_source_text ( cx) . map ( |t| t. to_string ( ) ) ,
409
450
_ => None ,
410
451
}
411
452
}
412
453
413
- fn suggest ( cx : & LateContext < ' _ > , suggestion : & Bool , terminals : & [ & Expr < ' _ > ] ) -> String {
454
+ fn suggest ( cx : & LateContext < ' _ > , msrv : & Msrv , suggestion : & Bool , terminals : & [ & Expr < ' _ > ] ) -> String {
414
455
let mut suggest_context = SuggestContext {
415
456
terminals,
416
457
cx,
458
+ msrv,
417
459
output : String :: new ( ) ,
418
460
} ;
419
461
suggest_context. recurse ( suggestion) ;
@@ -475,7 +517,7 @@ fn terminal_stats(b: &Bool) -> Stats {
475
517
stats
476
518
}
477
519
478
- impl < ' a , ' tcx > NonminimalBoolVisitor < ' a , ' tcx > {
520
+ impl < ' tcx > NonminimalBoolVisitor < ' _ , ' tcx > {
479
521
fn bool_expr ( & self , e : & ' tcx Expr < ' _ > ) {
480
522
let mut h2q = Hir2Qmm {
481
523
terminals : Vec :: new ( ) ,
@@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
526
568
diag. span_suggestion (
527
569
e. span ,
528
570
"it would look like the following" ,
529
- suggest ( self . cx , suggestion, & h2q. terminals ) ,
571
+ suggest ( self . cx , self . msrv , suggestion, & h2q. terminals ) ,
530
572
// nonminimal_bool can produce minimal but
531
573
// not human readable expressions (#3141)
532
574
Applicability :: Unspecified ,
@@ -569,20 +611,20 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
569
611
}
570
612
} ;
571
613
if improvements. is_empty ( ) {
572
- check_simplify_not ( self . cx , e) ;
614
+ check_simplify_not ( self . cx , self . msrv , e) ;
573
615
} else {
574
616
nonminimal_bool_lint (
575
617
improvements
576
618
. into_iter ( )
577
- . map ( |suggestion| suggest ( self . cx , suggestion, & h2q. terminals ) )
619
+ . map ( |suggestion| suggest ( self . cx , self . msrv , suggestion, & h2q. terminals ) )
578
620
. collect ( ) ,
579
621
) ;
580
622
}
581
623
}
582
624
}
583
625
}
584
626
585
- impl < ' a , ' tcx > Visitor < ' tcx > for NonminimalBoolVisitor < ' a , ' tcx > {
627
+ impl < ' tcx > Visitor < ' tcx > for NonminimalBoolVisitor < ' _ , ' tcx > {
586
628
fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
587
629
if !e. span . from_expansion ( ) {
588
630
match & e. kind {
0 commit comments