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 {
7795fn 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
89107fn 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 < ' _ > ) {
180200if 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{
186206span_lint_and_sugg (
@@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
197217
198218struct NonminimalBoolVisitor < ' a , ' tcx > {
199219cx : & ' a LateContext < ' tcx > ,
220+ msrv : & ' a Msrv ,
200221}
201222
202223use quine_mc_cluskey:: Bool ;
@@ -205,7 +226,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
205226cx : & ' a LateContext < ' tcx > ,
206227}
207228
208- impl < ' a , ' tcx , ' v > Hir2Qmm < ' a , ' tcx , ' v > {
229+ impl < ' v > Hir2Qmm < ' _ , ' _ , ' v > {
209230fn extract ( & mut self , op : BinOpKind , a : & [ & ' v Expr < ' _ > ] , mut v : Vec < Bool > ) -> Result < Vec < Bool > , String > {
210231for a in a {
211232if let ExprKind :: Binary ( binop, lhs, rhs) = & a. kind {
@@ -289,10 +310,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
289310struct SuggestContext < ' a , ' tcx , ' v > {
290311terminals : & ' v [ & ' v Expr < ' v > ] ,
291312cx : & ' a LateContext < ' tcx > ,
313+ msrv : & ' a Msrv ,
292314output : String ,
293315}
294316
295- impl < ' a , ' tcx , ' v > SuggestContext < ' a , ' tcx , ' v > {
317+ impl SuggestContext < ' _ , ' _ , ' _ > {
296318fn recurse ( & mut self , suggestion : & Bool ) -> Option < ( ) > {
297319use quine_mc_cluskey:: Bool :: { And , False , Not , Or , Term , True } ;
298320match suggestion {
@@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
311333} ,
312334Term ( n) => {
313335let 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) {
315337self . output . push_str ( & str) ;
316338} else {
317339self . 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 > {
362384match & expr. kind {
363385ExprKind :: Binary ( binop, lhs, rhs) => {
364386if !implements_ord ( cx, lhs) {
@@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
389411Some ( format ! ( "{lhs_snippet}{op}{rhs_snippet}" ) )
390412} )
391413} ,
392- ExprKind :: MethodCall ( path, receiver, [ ] , _) => {
414+ ExprKind :: MethodCall ( path, receiver, args , _) => {
393415let type_of_receiver = cx. typeck_results ( ) . expr_ty ( receiver) ;
394416if !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> {
399421METHODS_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 {
414455let mut suggest_context = SuggestContext {
415456 terminals,
416457 cx,
458+ msrv,
417459output : String :: new ( ) ,
418460} ;
419461 suggest_context. recurse ( suggestion) ;
@@ -475,7 +517,7 @@ fn terminal_stats(b: &Bool) -> Stats {
475517 stats
476518}
477519
478- impl < ' a , ' tcx > NonminimalBoolVisitor < ' a , ' tcx > {
520+ impl < ' tcx > NonminimalBoolVisitor < ' _ , ' tcx > {
479521fn bool_expr ( & self , e : & ' tcx Expr < ' _ > ) {
480522let mut h2q = Hir2Qmm {
481523terminals : Vec :: new ( ) ,
@@ -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)
532574Applicability :: Unspecified ,
@@ -569,20 +611,20 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
569611}
570612} ;
571613if improvements. is_empty ( ) {
572- check_simplify_not ( self . cx , e) ;
614+ check_simplify_not ( self . cx , self . msrv , e) ;
573615} else {
574616nonminimal_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}
581623}
582624}
583625}
584626
585- impl < ' a , ' tcx > Visitor < ' tcx > for NonminimalBoolVisitor < ' a , ' tcx > {
627+ impl < ' tcx > Visitor < ' tcx > for NonminimalBoolVisitor < ' _ , ' tcx > {
586628fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
587629if !e. span . from_expansion ( ) {
588630match & e. kind {
0 commit comments