1+ use clippy_config:: Conf ;
2+ use clippy_config:: msrvs:: { self , Msrv } ;
13use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
24use clippy_utils:: eq_expr_value;
35use clippy_utils:: source:: SpanRangeExt ;
@@ -7,7 +9,7 @@ use rustc_errors::Applicability;
79use rustc_hir:: intravisit:: { FnKind , Visitor , walk_expr} ;
810use rustc_hir:: { BinOpKind , Body , Expr , ExprKind , FnDecl , UnOp } ;
911use rustc_lint:: { LateContext , LateLintPass , Level } ;
10- use rustc_session:: declare_lint_pass ;
12+ use rustc_session:: { RustcVersion , impl_lint_pass } ;
1113use rustc_span:: def_id:: LocalDefId ;
1214use rustc_span:: { Span , sym} ;
1315
@@ -69,9 +71,25 @@ declare_clippy_lint! {
6971}
7072
7173// 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+ }
7391
74- declare_lint_pass ! ( NonminimalBool => [ NONMINIMAL_BOOL , OVERLY_COMPLEX_BOOL_EXPR ] ) ;
92+ impl_lint_pass ! ( NonminimalBool => [ NONMINIMAL_BOOL , OVERLY_COMPLEX_BOOL_EXPR ] ) ;
7593
7694impl < ' tcx > LateLintPass < ' tcx > for NonminimalBool {
7795 fn check_fn (
@@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
83101 _: Span ,
84102 _: LocalDefId ,
85103 ) {
86- NonminimalBoolVisitor { cx } . visit_body ( body) ;
104+ NonminimalBoolVisitor { cx, msrv : & self . msrv } . visit_body ( body) ;
87105 }
88106
89107 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
@@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
100118 _ => { } ,
101119 }
102120 }
121+
122+ extract_msrv_attr ! ( LateContext ) ;
103123}
104124
105125fn inverted_bin_op_eq_str ( op : BinOpKind ) -> Option < & ' static str > {
@@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition(
176196 ) ;
177197}
178198
179- fn check_simplify_not ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
199+ fn check_simplify_not ( cx : & LateContext < ' _ > , msrv : & Msrv , expr : & Expr < ' _ > ) {
180200 if let ExprKind :: Unary ( UnOp :: Not , inner) = & expr. kind
181201 && !expr. span . from_expansion ( )
182202 && !inner. span . from_expansion ( )
183- && let Some ( suggestion) = simplify_not ( cx, inner)
203+ && let Some ( suggestion) = simplify_not ( cx, msrv , inner)
184204 && cx. tcx . lint_level_at_node ( NONMINIMAL_BOOL , expr. hir_id ) . 0 != Level :: Allow
185205 {
186206 span_lint_and_sugg (
@@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
197217
198218struct NonminimalBoolVisitor < ' a , ' tcx > {
199219 cx : & ' a LateContext < ' tcx > ,
220+ msrv : & ' a Msrv ,
200221}
201222
202223use quine_mc_cluskey:: Bool ;
@@ -289,6 +310,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
289310struct SuggestContext < ' a , ' tcx , ' v > {
290311 terminals : & ' v [ & ' v Expr < ' v > ] ,
291312 cx : & ' a LateContext < ' tcx > ,
313+ msrv : & ' a Msrv ,
292314 output : String ,
293315}
294316
@@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
311333 } ,
312334 Term ( n) => {
313335 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) {
315337 self . output . push_str ( & str) ;
316338 } else {
317339 self . output . push ( '!' ) ;
@@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
358380 }
359381}
360382
361- fn simplify_not ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Option < String > {
383+ fn simplify_not ( cx : & LateContext < ' _ > , curr_msrv : & Msrv , expr : & Expr < ' _ > ) -> Option < String > {
362384 match & expr. kind {
363385 ExprKind :: Binary ( binop, lhs, rhs) => {
364386 if !implements_ord ( cx, lhs) {
@@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
389411 Some ( format ! ( "{lhs_snippet}{op}{rhs_snippet}" ) )
390412 } )
391413 } ,
392- ExprKind :: MethodCall ( path, receiver, [ ] , _) => {
414+ ExprKind :: MethodCall ( path, receiver, args , _) => {
393415 let type_of_receiver = cx. typeck_results ( ) . expr_ty ( receiver) ;
394416 if !is_type_diagnostic_item ( cx, type_of_receiver, sym:: Option )
395417 && !is_type_diagnostic_item ( cx, type_of_receiver, sym:: Result )
@@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
399421 METHODS_WITH_NEGATION
400422 . iter ( )
401423 . 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+ ) )
406436 } )
407- . and_then ( |( _, neg_method) | Some ( format ! ( "{}.{neg_method}()" , receiver. span. get_source_text( cx) ?) ) )
408437 } ,
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 ( ) ) ,
409450 _ => None ,
410451 }
411452}
412453
413- fn suggest ( cx : & LateContext < ' _ > , suggestion : & Bool , terminals : & [ & Expr < ' _ > ] ) -> String {
454+ fn suggest ( cx : & LateContext < ' _ > , msrv : & Msrv , suggestion : & Bool , terminals : & [ & Expr < ' _ > ] ) -> String {
414455 let mut suggest_context = SuggestContext {
415456 terminals,
416457 cx,
458+ msrv,
417459 output : String :: new ( ) ,
418460 } ;
419461 suggest_context. recurse ( suggestion) ;
@@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
526568 diag. span_suggestion (
527569 e. span ,
528570 "it would look like the following" ,
529- suggest ( self . cx , suggestion, & h2q. terminals ) ,
571+ suggest ( self . cx , self . msrv , suggestion, & h2q. terminals ) ,
530572 // nonminimal_bool can produce minimal but
531573 // not human readable expressions (#3141)
532574 Applicability :: Unspecified ,
@@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
569611 }
570612 } ;
571613 if improvements. is_empty ( ) {
572- check_simplify_not ( self . cx , e) ;
614+ check_simplify_not ( self . cx , self . msrv , e) ;
573615 } else {
574616 nonminimal_bool_lint (
575617 improvements
576618 . into_iter ( )
577- . map ( |suggestion| suggest ( self . cx , suggestion, & h2q. terminals ) )
619+ . map ( |suggestion| suggest ( self . cx , self . msrv , suggestion, & h2q. terminals ) )
578620 . collect ( ) ,
579621 ) ;
580622 }
0 commit comments