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! {
_ => 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
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>(