1 use super::{contains_return, BIND_INSTEAD_OF_MAP}
;
2 use clippy_utils
::diagnostics
::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then}
;
3 use clippy_utils
::source
::{snippet, snippet_with_context}
;
4 use clippy_utils
::{peel_blocks, visitors::find_all_ret_expressions}
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
8 use rustc_hir
::def
::{CtorKind, CtorOf, DefKind, Res}
;
9 use rustc_hir
::{LangItem, QPath}
;
10 use rustc_lint
::LateContext
;
13 pub(crate) struct OptionAndThenSome
;
15 impl BindInsteadOfMap
for OptionAndThenSome
{
16 const VARIANT_LANG_ITEM
: LangItem
= LangItem
::OptionSome
;
17 const BAD_METHOD_NAME
: &'
static str = "and_then";
18 const GOOD_METHOD_NAME
: &'
static str = "map";
21 pub(crate) struct ResultAndThenOk
;
23 impl BindInsteadOfMap
for ResultAndThenOk
{
24 const VARIANT_LANG_ITEM
: LangItem
= LangItem
::ResultOk
;
25 const BAD_METHOD_NAME
: &'
static str = "and_then";
26 const GOOD_METHOD_NAME
: &'
static str = "map";
29 pub(crate) struct ResultOrElseErrInfo
;
31 impl BindInsteadOfMap
for ResultOrElseErrInfo
{
32 const VARIANT_LANG_ITEM
: LangItem
= LangItem
::ResultErr
;
33 const BAD_METHOD_NAME
: &'
static str = "or_else";
34 const GOOD_METHOD_NAME
: &'
static str = "map_err";
37 pub(crate) trait BindInsteadOfMap
{
38 const VARIANT_LANG_ITEM
: LangItem
;
39 const BAD_METHOD_NAME
: &'
static str;
40 const GOOD_METHOD_NAME
: &'
static str;
42 fn no_op_msg(cx
: &LateContext
<'_
>) -> Option
<String
> {
43 let variant_id
= cx
.tcx
.lang_items().get(Self::VARIANT_LANG_ITEM
)?
;
44 let item_id
= cx
.tcx
.parent(variant_id
);
46 "using `{}.{}({})`, which is a no-op",
47 cx
.tcx
.item_name(item_id
),
48 Self::BAD_METHOD_NAME
,
49 cx
.tcx
.item_name(variant_id
),
53 fn lint_msg(cx
: &LateContext
<'_
>) -> Option
<String
> {
54 let variant_id
= cx
.tcx
.lang_items().get(Self::VARIANT_LANG_ITEM
)?
;
55 let item_id
= cx
.tcx
.parent(variant_id
);
57 "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`",
58 cx
.tcx
.item_name(item_id
),
59 Self::BAD_METHOD_NAME
,
60 cx
.tcx
.item_name(variant_id
),
61 Self::GOOD_METHOD_NAME
65 fn lint_closure_autofixable(
69 closure_expr
: &hir
::Expr
<'_
>,
70 closure_args_span
: Span
,
73 if let hir
::ExprKind
::Call(some_expr
, [inner_expr
]) = closure_expr
.kind
;
74 if let hir
::ExprKind
::Path(QPath
::Resolved(_
, path
)) = some_expr
.kind
;
75 if Self::is_variant(cx
, path
.res
);
76 if !contains_return(inner_expr
);
77 if let Some(msg
) = Self::lint_msg(cx
);
79 let mut app
= Applicability
::MachineApplicable
;
80 let some_inner_snip
= snippet_with_context(cx
, inner_expr
.span
, closure_expr
.span
.ctxt(), "_", &mut app
).0;
82 let closure_args_snip
= snippet(cx
, closure_args_span
, "..");
83 let option_snip
= snippet(cx
, recv
.span
, "..");
84 let note
= format
!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME
);
101 fn lint_closure(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>, closure_expr
: &hir
::Expr
<'_
>) -> bool
{
102 let mut suggs
= Vec
::new();
103 let can_sugg
: bool
= find_all_ret_expressions(cx
, closure_expr
, |ret_expr
| {
105 if !ret_expr
.span
.from_expansion();
106 if let hir
::ExprKind
::Call(func_path
, [arg
]) = ret_expr
.kind
;
107 if let hir
::ExprKind
::Path(QPath
::Resolved(_
, path
)) = func_path
.kind
;
108 if Self::is_variant(cx
, path
.res
);
109 if !contains_return(arg
);
111 suggs
.push((ret_expr
.span
, arg
.span
.source_callsite()));
118 let (span
, msg
) = if_chain
! {
120 if let hir
::ExprKind
::MethodCall(segment
, ..) = expr
.kind
;
121 if let Some(msg
) = Self::lint_msg(cx
);
122 then { (segment.ident.span, msg) }
else { return false; }
124 span_lint_and_then(cx
, BIND_INSTEAD_OF_MAP
, expr
.span
, &msg
, |diag
| {
125 multispan_sugg_with_applicability(
128 Applicability
::MachineApplicable
,
129 std
::iter
::once((span
, Self::GOOD_METHOD_NAME
.into())).chain(
132 .map(|(span1
, span2
)| (span1
, snippet(cx
, span2
, "_").into())),
139 /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
140 fn check(cx
: &LateContext
<'_
>, expr
: &hir
::Expr
<'_
>, recv
: &hir
::Expr
<'_
>, arg
: &hir
::Expr
<'_
>) -> bool
{
142 if let Some(adt
) = cx
.typeck_results().expr_ty(recv
).ty_adt_def();
143 if let Some(vid
) = cx
.tcx
.lang_items().get(Self::VARIANT_LANG_ITEM
);
144 if adt
.did() == cx
.tcx
.parent(vid
);
145 then {}
else { return false; }
149 hir
::ExprKind
::Closure(&hir
::Closure { body, fn_decl_span, .. }
) => {
150 let closure_body
= cx
.tcx
.hir().body(body
);
151 let closure_expr
= peel_blocks(closure_body
.value
);
153 if Self::lint_closure_autofixable(cx
, expr
, recv
, closure_expr
, fn_decl_span
) {
156 Self::lint_closure(cx
, expr
, closure_expr
)
159 // `_.and_then(Some)` case, which is no-op.
160 hir
::ExprKind
::Path(QPath
::Resolved(_
, path
)) if Self::is_variant(cx
, path
.res
) => {
161 if let Some(msg
) = Self::no_op_msg(cx
) {
167 "use the expression directly",
168 snippet(cx
, recv
.span
, "..").into(),
169 Applicability
::MachineApplicable
,
178 fn is_variant(cx
: &LateContext
<'_
>, res
: Res
) -> bool
{
179 if let Res
::Def(DefKind
::Ctor(CtorOf
::Variant
, CtorKind
::Fn
), id
) = res
{
180 if let Some(variant_id
) = cx
.tcx
.lang_items().get(Self::VARIANT_LANG_ITEM
) {
181 return cx
.tcx
.parent(id
) == variant_id
;