about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAsuna <SpriteOvO@gmail.com>2024-09-24 01:28:02 +0800
committerAsuna <SpriteOvO@gmail.com>2024-09-30 03:36:41 +0800
commit762a91b40e9f37f1b649f3a879e4639af32da2ed (patch)
treef22caf75a8bd1cf93b6c8ad094bee6f503e26a4e
parentb39323bf255fa79b20f75b7650d64b91d2e0517e (diff)
downloadrust-762a91b40e9f37f1b649f3a879e4639af32da2ed.tar.gz
rust-762a91b40e9f37f1b649f3a879e4639af32da2ed.zip
Simplify negative `Option::{is_some_and,is_none_or}`
-rw-r--r--clippy_config/src/msrvs.rs1
-rw-r--r--clippy_lints/src/booleans.rs78
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--tests/ui/nonminimal_bool_methods.fixed62
-rw-r--r--tests/ui/nonminimal_bool_methods.rs62
-rw-r--r--tests/ui/nonminimal_bool_methods.stderr110
-rw-r--r--tests/ui/nonminimal_bool_methods_unfixable.rs9
-rw-r--r--tests/ui/nonminimal_bool_methods_unfixable.stderr17
8 files changed, 321 insertions, 20 deletions
diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs
index e30df3d3234..68a3b11d384 100644
--- a/clippy_config/src/msrvs.rs
+++ b/clippy_config/src/msrvs.rs
@@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
 msrv_aliases! {
     1,83,0 { CONST_EXTERN_FN }
     1,83,0 { CONST_FLOAT_BITS_CONV }
+    1,82,0 { IS_NONE_OR }
     1,81,0 { LINT_REASONS_STABILIZATION }
     1,80,0 { BOX_INTO_ITER}
     1,77,0 { C_STR_LITERALS }
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index 3c2af72624f..160f0c6cf10 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -1,3 +1,5 @@
+use clippy_config::Conf;
+use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::eq_expr_value;
 use clippy_utils::source::SpanRangeExt;
@@ -7,7 +9,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
 use rustc_lint::{LateContext, LateLintPass, Level};
-use rustc_session::declare_lint_pass;
+use rustc_session::{RustcVersion, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, sym};
 
@@ -69,9 +71,25 @@ declare_clippy_lint! {
 }
 
 // For each pairs, both orders are considered.
-const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
+const METHODS_WITH_NEGATION: [(Option<RustcVersion>, &str, &str); 3] = [
+    (None, "is_some", "is_none"),
+    (None, "is_err", "is_ok"),
+    (Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"),
+];
+
+pub struct NonminimalBool {
+    msrv: Msrv,
+}
+
+impl NonminimalBool {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
 
-declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
+impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
 
 impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
     fn check_fn(
@@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
         _: Span,
         _: LocalDefId,
     ) {
-        NonminimalBoolVisitor { cx }.visit_body(body);
+        NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body);
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
             _ => {},
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
@@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition(
     );
 }
 
-fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
     if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
         && !expr.span.from_expansion()
         && !inner.span.from_expansion()
-        && let Some(suggestion) = simplify_not(cx, inner)
+        && let Some(suggestion) = simplify_not(cx, msrv, inner)
         && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
     {
         span_lint_and_sugg(
@@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
 struct NonminimalBoolVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
+    msrv: &'a Msrv,
 }
 
 use quine_mc_cluskey::Bool;
@@ -289,6 +310,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
 struct SuggestContext<'a, 'tcx, 'v> {
     terminals: &'v [&'v Expr<'v>],
     cx: &'a LateContext<'tcx>,
+    msrv: &'a Msrv,
     output: String,
 }
 
@@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
                 },
                 Term(n) => {
                     let terminal = self.terminals[n as usize];
-                    if let Some(str) = simplify_not(self.cx, terminal) {
+                    if let Some(str) = simplify_not(self.cx, self.msrv, terminal) {
                         self.output.push_str(&str);
                     } else {
                         self.output.push('!');
@@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
     }
 }
 
-fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option<String> {
     match &expr.kind {
         ExprKind::Binary(binop, lhs, rhs) => {
             if !implements_ord(cx, lhs) {
@@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
                 Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
             })
         },
-        ExprKind::MethodCall(path, receiver, [], _) => {
+        ExprKind::MethodCall(path, receiver, args, _) => {
             let type_of_receiver = cx.typeck_results().expr_ty(receiver);
             if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
                 && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
@@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
             METHODS_WITH_NEGATION
                 .iter()
                 .copied()
-                .flat_map(|(a, b)| vec![(a, b), (b, a)])
-                .find(|&(a, _)| {
-                    let path: &str = path.ident.name.as_str();
-                    a == path
+                .flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
+                .find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str())
+                .and_then(|(_, _, neg_method)| {
+                    let negated_args = args
+                        .iter()
+                        .map(|arg| simplify_not(cx, curr_msrv, arg))
+                        .collect::<Option<Vec<_>>>()?
+                        .join(", ");
+                    Some(format!(
+                        "{}.{neg_method}({negated_args})",
+                        receiver.span.get_source_text(cx)?
+                    ))
                 })
-                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
         },
+        ExprKind::Closure(closure) => {
+            let body = cx.tcx.hir().body(closure.body);
+            let params = body
+                .params
+                .iter()
+                .map(|param| param.span.get_source_text(cx).map(|t| t.to_string()))
+                .collect::<Option<Vec<_>>>()?
+                .join(", ");
+            let negated = simplify_not(cx, curr_msrv, body.value)?;
+            Some(format!("|{params}| {negated}"))
+        },
+        ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()),
         _ => None,
     }
 }
 
-fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
+fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
     let mut suggest_context = SuggestContext {
         terminals,
         cx,
+        msrv,
         output: String::new(),
     };
     suggest_context.recurse(suggestion);
@@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                                 diag.span_suggestion(
                                     e.span,
                                     "it would look like the following",
-                                    suggest(self.cx, suggestion, &h2q.terminals),
+                                    suggest(self.cx, self.msrv, suggestion, &h2q.terminals),
                                     // nonminimal_bool can produce minimal but
                                     // not human readable expressions (#3141)
                                     Applicability::Unspecified,
@@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             };
             if improvements.is_empty() {
-                check_simplify_not(self.cx, e);
+                check_simplify_not(self.cx, self.msrv, e);
             } else {
                 nonminimal_bool_lint(
                     improvements
                         .into_iter()
-                        .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
+                        .map(|suggestion| suggest(self.cx, self.msrv, suggestion, &h2q.terminals))
                         .collect(),
                 );
             }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 1d41f568f37..2eb6d99b761 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -609,7 +609,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf)));
     store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
     store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
-    store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
+    store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
     store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
     store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
     store.register_late_pass(|_| Box::new(ptr::Ptr));
diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed
index cc91ba6ec66..a23310c1ad9 100644
--- a/tests/ui/nonminimal_bool_methods.fixed
+++ b/tests/ui/nonminimal_bool_methods.fixed
@@ -115,4 +115,66 @@ fn issue_12625() {
     if a as u64 > b {} //~ ERROR: this boolean expression can be simplified
 }
 
+fn issue_13436() {
+    fn not_zero(x: i32) -> bool {
+        x != 0
+    }
+
+    let opt = Some(500);
+    _ = opt.is_some_and(|x| x < 1000);
+    _ = opt.is_some_and(|x| x <= 1000);
+    _ = opt.is_some_and(|x| x > 1000);
+    _ = opt.is_some_and(|x| x >= 1000);
+    _ = opt.is_some_and(|x| x == 1000);
+    _ = opt.is_some_and(|x| x != 1000);
+    _ = opt.is_some_and(not_zero);
+    _ = opt.is_none_or(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x > 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x < 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x != 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x == 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(not_zero);
+    _ = opt.is_none_or(|x| x < 1000);
+    _ = opt.is_none_or(|x| x <= 1000);
+    _ = opt.is_none_or(|x| x > 1000);
+    _ = opt.is_none_or(|x| x >= 1000);
+    _ = opt.is_none_or(|x| x == 1000);
+    _ = opt.is_none_or(|x| x != 1000);
+    _ = opt.is_none_or(not_zero);
+    _ = opt.is_some_and(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x > 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x < 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x != 1000); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x == 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(not_zero);
+
+    let opt = Some(true);
+    _ = opt.is_some_and(|x| x);
+    _ = opt.is_some_and(|x| !x);
+    _ = !opt.is_some_and(|x| x);
+    _ = opt.is_none_or(|x| x); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x);
+    _ = opt.is_none_or(|x| !x);
+    _ = !opt.is_none_or(|x| x);
+    _ = opt.is_some_and(|x| x); //~ ERROR: this boolean expression can be simplified
+
+    let opt: Option<Result<i32, i32>> = Some(Ok(123));
+    _ = opt.is_some_and(|x| x.is_ok());
+    _ = opt.is_some_and(|x| x.is_err());
+    _ = opt.is_none_or(|x| x.is_ok());
+    _ = opt.is_none_or(|x| x.is_err());
+    _ = opt.is_none_or(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_some_and(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified
+
+    #[clippy::msrv = "1.81"]
+    fn before_stabilization() {
+        let opt = Some(500);
+        _ = !opt.is_some_and(|x| x < 1000);
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs
index c812f6f0ca4..6c844373af7 100644
--- a/tests/ui/nonminimal_bool_methods.rs
+++ b/tests/ui/nonminimal_bool_methods.rs
@@ -115,4 +115,66 @@ fn issue_12625() {
     if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified
 }
 
+fn issue_13436() {
+    fn not_zero(x: i32) -> bool {
+        x != 0
+    }
+
+    let opt = Some(500);
+    _ = opt.is_some_and(|x| x < 1000);
+    _ = opt.is_some_and(|x| x <= 1000);
+    _ = opt.is_some_and(|x| x > 1000);
+    _ = opt.is_some_and(|x| x >= 1000);
+    _ = opt.is_some_and(|x| x == 1000);
+    _ = opt.is_some_and(|x| x != 1000);
+    _ = opt.is_some_and(not_zero);
+    _ = !opt.is_some_and(|x| x < 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x > 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x == 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x != 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(not_zero);
+    _ = opt.is_none_or(|x| x < 1000);
+    _ = opt.is_none_or(|x| x <= 1000);
+    _ = opt.is_none_or(|x| x > 1000);
+    _ = opt.is_none_or(|x| x >= 1000);
+    _ = opt.is_none_or(|x| x == 1000);
+    _ = opt.is_none_or(|x| x != 1000);
+    _ = opt.is_none_or(not_zero);
+    _ = !opt.is_none_or(|x| x < 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x <= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x > 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x >= 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x == 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x != 1000); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(not_zero);
+
+    let opt = Some(true);
+    _ = opt.is_some_and(|x| x);
+    _ = opt.is_some_and(|x| !x);
+    _ = !opt.is_some_and(|x| x);
+    _ = !opt.is_some_and(|x| !x); //~ ERROR: this boolean expression can be simplified
+    _ = opt.is_none_or(|x| x);
+    _ = opt.is_none_or(|x| !x);
+    _ = !opt.is_none_or(|x| x);
+    _ = !opt.is_none_or(|x| !x); //~ ERROR: this boolean expression can be simplified
+
+    let opt: Option<Result<i32, i32>> = Some(Ok(123));
+    _ = opt.is_some_and(|x| x.is_ok());
+    _ = opt.is_some_and(|x| x.is_err());
+    _ = opt.is_none_or(|x| x.is_ok());
+    _ = opt.is_none_or(|x| x.is_err());
+    _ = !opt.is_some_and(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_some_and(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x.is_ok()); //~ ERROR: this boolean expression can be simplified
+    _ = !opt.is_none_or(|x| x.is_err()); //~ ERROR: this boolean expression can be simplified
+
+    #[clippy::msrv = "1.81"]
+    fn before_stabilization() {
+        let opt = Some(500);
+        _ = !opt.is_some_and(|x| x < 1000);
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr
index d7adc0638b3..52803e828ae 100644
--- a/tests/ui/nonminimal_bool_methods.stderr
+++ b/tests/ui/nonminimal_bool_methods.stderr
@@ -97,5 +97,113 @@ error: this boolean expression can be simplified
 LL |     if !(a as u64 <= b) {}
    |        ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b`
 
-error: aborting due to 16 previous errors
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:131:9
+   |
+LL |     _ = !opt.is_some_and(|x| x < 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x >= 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:132:9
+   |
+LL |     _ = !opt.is_some_and(|x| x <= 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x > 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:133:9
+   |
+LL |     _ = !opt.is_some_and(|x| x > 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x <= 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:134:9
+   |
+LL |     _ = !opt.is_some_and(|x| x >= 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x < 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:135:9
+   |
+LL |     _ = !opt.is_some_and(|x| x == 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x != 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:136:9
+   |
+LL |     _ = !opt.is_some_and(|x| x != 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x == 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:145:9
+   |
+LL |     _ = !opt.is_none_or(|x| x < 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x >= 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:146:9
+   |
+LL |     _ = !opt.is_none_or(|x| x <= 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x > 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:147:9
+   |
+LL |     _ = !opt.is_none_or(|x| x > 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x <= 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:148:9
+   |
+LL |     _ = !opt.is_none_or(|x| x >= 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x < 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:149:9
+   |
+LL |     _ = !opt.is_none_or(|x| x == 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x != 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:150:9
+   |
+LL |     _ = !opt.is_none_or(|x| x != 1000);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x == 1000)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:157:9
+   |
+LL |     _ = !opt.is_some_and(|x| !x);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:161:9
+   |
+LL |     _ = !opt.is_none_or(|x| !x);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:168:9
+   |
+LL |     _ = !opt.is_some_and(|x| x.is_ok());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_err())`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:169:9
+   |
+LL |     _ = !opt.is_some_and(|x| x.is_err());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_ok())`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:170:9
+   |
+LL |     _ = !opt.is_none_or(|x| x.is_ok());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_err())`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:171:9
+   |
+LL |     _ = !opt.is_none_or(|x| x.is_err());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())`
+
+error: aborting due to 34 previous errors
 
diff --git a/tests/ui/nonminimal_bool_methods_unfixable.rs b/tests/ui/nonminimal_bool_methods_unfixable.rs
new file mode 100644
index 00000000000..60b8da30a2f
--- /dev/null
+++ b/tests/ui/nonminimal_bool_methods_unfixable.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::nonminimal_bool)]
+//@no-rustfix
+
+fn issue_13436() {
+    let opt_opt = Some(Some(500));
+    _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000)); //~ ERROR: this boolean expression can be simplified
+}
+
+fn main() {}
diff --git a/tests/ui/nonminimal_bool_methods_unfixable.stderr b/tests/ui/nonminimal_bool_methods_unfixable.stderr
new file mode 100644
index 00000000000..5a90155844c
--- /dev/null
+++ b/tests/ui/nonminimal_bool_methods_unfixable.stderr
@@ -0,0 +1,17 @@
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods_unfixable.rs:6:9
+   |
+LL |     _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt_opt.is_none_or(|x| x.is_some_and(|y| y != 1000))`
+   |
+   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods_unfixable.rs:6:34
+   |
+LL |     _ = !opt_opt.is_some_and(|x| !x.is_some_and(|y| y != 1000));
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_none_or(|y| y == 1000)`
+
+error: aborting due to 2 previous errors
+