Skip to content

Commit 7854830

Browse files
committed
Adding extra case for question_mark
1 parent af1f78a commit 7854830

File tree

1 file changed

+92
-3
lines changed

1 file changed

+92
-3
lines changed

clippy_lints/src/question_mark.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,21 @@ use clippy_utils::{
1111
pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
1212
span_contains_cfg, span_contains_comment,
1313
};
14+
use itertools::Itertools;
1415
use rustc_errors::Applicability;
1516
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
1617
use rustc_hir::def::Res;
18+
use rustc_hir::intravisit::FnKind;
1719
use rustc_hir::{
18-
Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnRetTy, HirId, LetStmt, MatchSource, Mutability, Node, Pat,
19-
PatKind, PathSegment, QPath, Stmt, StmtKind,
20+
Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnDecl, FnRetTy, HirId, LetStmt, MatchSource, Mutability,
21+
Node, Pat, PatKind, PathSegment, QPath, Stmt, StmtKind,
2022
};
2123
use rustc_lint::{LateContext, LateLintPass};
2224
use rustc_middle::ty::{self, Ty};
2325
use rustc_session::impl_lint_pass;
24-
use rustc_span::sym;
26+
use rustc_span::def_id::LocalDefId;
2527
use rustc_span::symbol::Symbol;
28+
use rustc_span::{Span, sym};
2629

2730
declare_clippy_lint! {
2831
/// ### What it does
@@ -456,6 +459,61 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
456459
}
457460
}
458461

462+
fn check_if_let_some_as_return_val<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
463+
if let Some(higher::IfLet {
464+
let_pat,
465+
let_expr,
466+
if_then,
467+
if_else,
468+
..
469+
}) = higher::IfLet::hir(cx, expr)
470+
&& let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind
471+
&& ddpos.as_opt_usize().is_none()
472+
&& let PatKind::Binding(BindingMode(by_ref, _), _, ident, None) = field.kind
473+
&& let caller_ty = cx.typeck_results().expr_ty(let_expr)
474+
&& let if_block = IfBlockType::IfLet(
475+
cx.qpath_res(path1, let_pat.hir_id),
476+
caller_ty,
477+
ident.name,
478+
let_expr,
479+
if_then,
480+
if_else,
481+
)
482+
&& let ExprKind::Block(if_then_block, _) = if_then.kind
483+
// Don't consider case where if-then branch has only one statement/expression
484+
&& (if_then_block.stmts.len() >= 2 || if_then_block.stmts.len() == 1 && if_then_block.expr.is_some())
485+
&& (is_early_return(sym::Option, cx, &if_block))
486+
&& if_else
487+
.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e)))
488+
.filter(|e| *e)
489+
.is_none()
490+
{
491+
let mut applicability = Applicability::MachineApplicable;
492+
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
493+
let method_call_str = match by_ref {
494+
ByRef::Yes(Mutability::Mut) => ".as_mut()",
495+
ByRef::Yes(Mutability::Not) => ".as_ref()",
496+
ByRef::No => "",
497+
};
498+
499+
let body = snippet_with_applicability(cx, if_then.span, "..", &mut applicability);
500+
let mut body_lines = body.lines().map(|line| &line[line.len().min(4)..]);
501+
let (_, _) = (body_lines.next(), body_lines.next_back());
502+
let body_str = body_lines.join("\n");
503+
504+
let sugg = format!("let {ident} = {receiver_str}{method_call_str}?;\n{body_str}",);
505+
span_lint_and_sugg(
506+
cx,
507+
QUESTION_MARK,
508+
expr.span,
509+
"this block may be rewritten with the `?` operator",
510+
"replace it with",
511+
sugg,
512+
applicability,
513+
);
514+
}
515+
}
516+
459517
impl QuestionMark {
460518
fn inside_try_block(&self) -> bool {
461519
self.try_block_depth_stack.last() > Some(&0)
@@ -531,6 +589,37 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
531589
self.try_block_depth_stack.push(0);
532590
}
533591

592+
// Defining check_fn instead of using check_body to avoid accidentally triggering on
593+
// const expressions, not sure if this is necessary
594+
fn check_fn(
595+
&mut self,
596+
cx: &LateContext<'tcx>,
597+
_: FnKind<'tcx>,
598+
_: &'tcx FnDecl<'tcx>,
599+
body: &'tcx Body<'tcx>,
600+
_: Span,
601+
_: LocalDefId,
602+
) {
603+
let return_expr = match body.value.kind {
604+
ExprKind::Block(block, _) => block.expr.or_else(|| {
605+
block.stmts.iter().last().and_then(|stmt| {
606+
if let StmtKind::Semi(expr) = stmt.kind
607+
&& let ExprKind::Ret(Some(ret)) = expr.kind
608+
{
609+
Some(ret)
610+
} else {
611+
None
612+
}
613+
})
614+
}),
615+
_ => None,
616+
};
617+
618+
if let Some(expr) = return_expr {
619+
check_if_let_some_as_return_val(cx, expr);
620+
}
621+
}
622+
534623
fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) {
535624
self.try_block_depth_stack.pop();
536625
}

0 commit comments

Comments
 (0)