1 use super::utils
::make_iterator_snippet
;
2 use super::MANUAL_FIND
;
3 use clippy_utils
::diagnostics
::span_lint_and_then
;
4 use clippy_utils
::source
::snippet_with_applicability
;
5 use clippy_utils
::ty
::implements_trait
;
6 use clippy_utils
::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}
;
7 use if_chain
::if_chain
;
8 use rustc_errors
::Applicability
;
9 use rustc_hir
::def
::Res
;
10 use rustc_hir
::lang_items
::LangItem
;
11 use rustc_hir
::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}
;
12 use rustc_lint
::LateContext
;
15 pub(super) fn check
<'tcx
>(
16 cx
: &LateContext
<'tcx
>,
23 let inner_expr
= peel_blocks_with_stmt(body
);
24 // Check for the specific case that the result is returned and optimize suggestion for that (more
25 // cases can be added later)
27 if let Some(higher
::If { cond, then, r#else: None, }
) = higher
::If
::hir(inner_expr
);
28 if let Some(binding_id
) = get_binding(pat
);
29 if let ExprKind
::Block(block
, _
) = then
.kind
;
30 if let [stmt
] = block
.stmts
;
31 if let StmtKind
::Semi(semi
) = stmt
.kind
;
32 if let ExprKind
::Ret(Some(ret_value
)) = semi
.kind
;
33 if let ExprKind
::Call(ctor
, [inner_ret
]) = ret_value
.kind
;
34 if is_res_lang_ctor(cx
, path_res(cx
, ctor
), LangItem
::OptionSome
);
35 if path_res(cx
, inner_ret
) == Res
::Local(binding_id
);
36 if let Some((last_stmt
, last_ret
)) = last_stmt_and_ret(cx
, expr
);
38 let mut applicability
= Applicability
::MachineApplicable
;
39 let mut snippet
= make_iterator_snippet(cx
, arg
, &mut applicability
);
40 // Checks if `pat` is a single reference to a binding (`&x`)
41 let is_ref_to_binding
=
42 matches
!(pat
.kind
, PatKind
::Ref(inner
, _
) if matches
!(inner
.kind
, PatKind
::Binding(..)));
43 // If `pat` is not a binding or a reference to a binding (`x` or `&x`)
44 // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
45 if !(matches
!(pat
.kind
, PatKind
::Binding(..)) || is_ref_to_binding
) {
49 snippet_with_applicability(cx
, pat
.span
, "..", &mut applicability
),
50 snippet_with_applicability(cx
, inner_ret
.span
, "..", &mut applicability
),
54 let ty
= cx
.typeck_results().expr_ty(inner_ret
);
55 if cx
.tcx
.lang_items().copy_trait().map_or(false, |id
| implements_trait(cx
, ty
, id
, &[])) {
59 "&".repeat(1 + usize::from(is_ref_to_binding
)),
60 snippet_with_applicability(cx
, inner_ret
.span
, "..", &mut applicability
),
61 snippet_with_applicability(cx
, cond
.span
, "..", &mut applicability
),
64 if is_ref_to_binding
{
65 snippet
.push_str(".copied()");
68 applicability
= Applicability
::MaybeIncorrect
;
72 snippet_with_applicability(cx
, inner_ret
.span
, "..", &mut applicability
),
73 snippet_with_applicability(cx
, cond
.span
, "..", &mut applicability
),
77 // Extends to `last_stmt` to include semicolon in case of `return None;`
78 let lint_span
= span
.to(last_stmt
.span
).to(last_ret
.span
);
83 "manual implementation of `Iterator::find`",
85 if applicability
== Applicability
::MaybeIncorrect
{
86 diag
.note("you may need to dereference some variables");
90 "replace with an iterator",
100 fn get_binding(pat
: &Pat
<'_
>) -> Option
<HirId
> {
101 let mut hir_id
= None
;
103 pat
.each_binding(|annotation
, id
, _
, _
| {
109 if let BindingAnnotation
::NONE
= annotation
{
116 // Returns the last statement and last return if function fits format for lint
117 fn last_stmt_and_ret
<'tcx
>(
118 cx
: &LateContext
<'tcx
>,
119 expr
: &'tcx Expr
<'_
>,
120 ) -> Option
<(&'tcx Stmt
<'tcx
>, &'tcx Expr
<'tcx
>)> {
121 // Returns last non-return statement and the last return
122 fn extract
<'tcx
>(block
: &Block
<'tcx
>) -> Option
<(&'tcx Stmt
<'tcx
>, &'tcx Expr
<'tcx
>)> {
123 if let [.., last_stmt
] = block
.stmts
{
124 if let Some(ret
) = block
.expr
{
125 return Some((last_stmt
, ret
));
128 if let [.., snd_last
, _
] = block
.stmts
;
129 if let StmtKind
::Semi(last_expr
) = last_stmt
.kind
;
130 if let ExprKind
::Ret(Some(ret
)) = last_expr
.kind
;
132 return Some((snd_last
, ret
));
138 let mut parent_iter
= cx
.tcx
.hir().parent_iter(expr
.hir_id
);
140 // This should be the loop
141 if let Some((node_hir
, Node
::Stmt(..))) = parent_iter
.next();
142 // This should be the function body
143 if let Some((_
, Node
::Block(block
))) = parent_iter
.next();
144 if let Some((last_stmt
, last_ret
)) = extract(block
);
145 if last_stmt
.hir_id
== node_hir
;
146 if is_res_lang_ctor(cx
, path_res(cx
, last_ret
), LangItem
::OptionNone
);
147 if let Some((_
, Node
::Expr(_block
))) = parent_iter
.next();
148 // This includes the function header
149 if let Some((_
, func
)) = parent_iter
.next();
150 if func
.fn_kind().is_some();
152 Some((block
.stmts
.last().unwrap(), last_ret
))