]> git.proxmox.com Git - rustc.git/blobdiff - src/tools/clippy/clippy_lints/src/option_if_let_else.rs
New upstream version 1.76.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / option_if_let_else.rs
index d7cbbe13a261143c1383e202cd7418cc47c8d429..89e4e3c740d5eb06b84e8d7b9bd1c287137c0614 100644 (file)
@@ -4,13 +4,12 @@ use clippy_utils::{
     can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks,
     peel_hir_expr_while, CaptureKind,
 };
-use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::declare_lint_pass;
 use rustc_span::SyntaxContext;
 
 declare_clippy_lint! {
@@ -122,73 +121,97 @@ fn try_get_option_occurrence<'tcx>(
         _ => expr,
     };
     let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?;
-    if_chain! {
-        if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
-        if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
-        if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
-        if some_captures
+    if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind
+        && let Some(some_captures) = can_move_expr_to_closure(cx, if_then)
+        && let Some(none_captures) = can_move_expr_to_closure(cx, if_else)
+        && some_captures
             .iter()
             .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
-            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
-        then {
-            let capture_mut = if bind_annotation == BindingAnnotation::MUT { "mut " } else { "" };
-            let some_body = peel_blocks(if_then);
-            let none_body = peel_blocks(if_else);
-            let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
-            let capture_name = id.name.to_ident_string();
-            let (as_ref, as_mut) = match &expr.kind {
-                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
-                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
-                _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => {
-                    (mutb == Mutability::Not, mutb == Mutability::Mut)
-                }
-                _ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT),
-            };
+            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref())
+    {
+        let capture_mut = if bind_annotation == BindingAnnotation::MUT {
+            "mut "
+        } else {
+            ""
+        };
+        let some_body = peel_blocks(if_then);
+        let none_body = peel_blocks(if_else);
+        let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) {
+            "map_or"
+        } else {
+            "map_or_else"
+        };
+        let capture_name = id.name.to_ident_string();
+        let (as_ref, as_mut) = match &expr.kind {
+            ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
+            ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
+            _ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => {
+                (mutb == Mutability::Not, mutb == Mutability::Mut)
+            },
+            _ => (
+                bind_annotation == BindingAnnotation::REF,
+                bind_annotation == BindingAnnotation::REF_MUT,
+            ),
+        };
 
-            // Check if captures the closure will need conflict with borrows made in the scrutinee.
-            // TODO: check all the references made in the scrutinee expression. This will require interacting
-            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
-            if as_ref || as_mut {
-                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
-                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
-                    _ => None,
-                });
-                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
-                    match some_captures.get(local_id)
-                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id)))
-                    {
-                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
-                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
-                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
-                    }
+        // Check if captures the closure will need conflict with borrows made in the scrutinee.
+        // TODO: check all the references made in the scrutinee expression. This will require interacting
+        // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
+        if as_ref || as_mut {
+            let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
+                ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
+                _ => None,
+            });
+            if let ExprKind::Path(QPath::Resolved(
+                None,
+                Path {
+                    res: Res::Local(local_id),
+                    ..
+                },
+            )) = e.kind
+            {
+                match some_captures.get(local_id).or_else(|| {
+                    (method_sugg == "map_or_else")
+                        .then_some(())
+                        .and_then(|()| none_captures.get(local_id))
+                }) {
+                    Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
+                    Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
+                    Some(CaptureKind::Ref(Mutability::Not)) | None => (),
                 }
             }
+        }
 
-            let mut app = Applicability::Unspecified;
+        let mut app = Applicability::Unspecified;
 
-            let (none_body, is_argless_call) = match none_body.kind {
-                ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true),
-                _ => (none_body, false),
-            };
+        let (none_body, is_argless_call) = match none_body.kind {
+            ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true),
+            _ => (none_body, false),
+        };
 
-            return Some(OptionOccurrence {
-                option: format_option_in_sugg(
-                    Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
-                    as_ref,
-                    as_mut,
-                ),
-                method_sugg: method_sugg.to_string(),
-                some_expr: format!(
-                    "|{capture_mut}{capture_name}| {}",
-                    Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app),
-                ),
-                none_expr: format!(
-                    "{}{}",
-                    if method_sugg == "map_or" || is_argless_call { "" } else if is_result { "|_| " } else { "|| "},
-                    Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
-                ),
-            });
-        }
+        return Some(OptionOccurrence {
+            option: format_option_in_sugg(
+                Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
+                as_ref,
+                as_mut,
+            ),
+            method_sugg: method_sugg.to_string(),
+            some_expr: format!(
+                "|{capture_mut}{capture_name}| {}",
+                Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app),
+            ),
+            none_expr: format!(
+                "{}{}",
+                if method_sugg == "map_or" || is_argless_call {
+                    ""
+                } else if is_result {
+                    "|_| "
+                } else {
+                    "|| "
+                },
+                Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
+            ),
+        });
     }
 
     None
@@ -216,21 +239,24 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
         if_then,
         if_else: Some(if_else),
     }) = higher::IfLet::hir(cx, expr)
+        && !cx.typeck_results().expr_ty(expr).is_unit()
+        && !is_else_clause(cx.tcx, expr)
     {
-        if !is_else_clause(cx.tcx, expr) {
-            return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else);
-        }
+        try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else)
+    } else {
+        None
     }
-    None
 }
 
 fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
-    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
-        if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
-            return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else);
-        }
+    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind
+        && !cx.typeck_results().expr_ty(expr).is_unit()
+        && let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms)
+    {
+        try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else)
+    } else {
+        None
     }
-    None
 }
 
 fn try_convert_match<'tcx>(