-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathreturn_and_then.rs
74 lines (64 loc) · 2.44 KB
/
return_and_then.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArg, Ty};
use rustc_span::sym;
use std::ops::ControlFlow;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::visitors::for_each_unconsumed_temporary;
use clippy_utils::{is_expr_final_block_expr, peel_blocks};
use super::RETURN_AND_THEN;
/// lint if `and_then` is the last expression in a block, and
/// there are no references or temporaries in the receiver
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &hir::Expr<'_>,
recv: &'tcx hir::Expr<'tcx>,
arg: &'tcx hir::Expr<'_>,
) {
if !is_expr_final_block_expr(cx.tcx, expr) {
return;
}
let recv_type = cx.typeck_results().expr_ty(recv);
if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) {
return;
}
let has_ref_type = matches!(recv_type.kind(), ty::Adt(_, args) if args
.first()
.and_then(|arg0: &GenericArg<'tcx>| GenericArg::as_type(*arg0))
.is_some_and(Ty::is_ref));
let has_temporaries = for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break();
if has_ref_type && has_temporaries {
return;
}
let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind else {
return;
};
let closure_arg = fn_decl.inputs[0];
let closure_expr = peel_blocks(cx.tcx.hir_body(body).value);
let mut applicability = Applicability::MachineApplicable;
let arg_snip = snippet_with_applicability(cx, closure_arg.span, "_", &mut applicability);
let recv_snip = snippet_with_applicability(cx, recv.span, "_", &mut applicability);
let body_snip = snippet_with_applicability(cx, closure_expr.span, "..", &mut applicability);
let inner = match body_snip.strip_prefix('{').and_then(|s| s.strip_suffix('}')) {
Some(s) => s.trim_start_matches('\n').trim_end(),
None => &body_snip,
};
let sugg = format!(
"let {} = {}?;\n{}",
arg_snip,
recv_snip,
reindent_multiline(inner, false, indent_of(cx, expr.span))
);
span_lint_and_sugg(
cx,
RETURN_AND_THEN,
expr.span,
"use the `?` operator instead of an `and_then` call",
"try",
sugg,
applicability,
);
}