1 use clippy_utils
::consts
::constant_simple
;
2 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
3 use clippy_utils
::source
::{indent_of, reindent_multiline, snippet_opt}
;
4 use clippy_utils
::ty
::is_type_diagnostic_item
;
5 use clippy_utils
::usage
::contains_return_break_continue_macro
;
6 use clippy_utils
::{in_constant, is_lang_ctor, path_to_local_id, sugg}
;
7 use if_chain
::if_chain
;
8 use rustc_errors
::Applicability
;
9 use rustc_hir
::LangItem
::{OptionNone, OptionSome, ResultErr, ResultOk}
;
10 use rustc_hir
::{Arm, Expr, ExprKind, PatKind}
;
11 use rustc_lint
::LintContext
;
12 use rustc_lint
::{LateContext, LateLintPass}
;
13 use rustc_middle
::lint
::in_external_macro
;
14 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
17 declare_clippy_lint
! {
19 /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
21 /// ### Why is this bad?
22 /// Concise code helps focusing on behavior instead of boilerplate.
26 /// let foo: Option<i32> = None;
35 /// let foo: Option<i32> = None;
40 "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
43 declare_lint_pass
!(ManualUnwrapOr
=> [MANUAL_UNWRAP_OR
]);
45 impl LateLintPass
<'_
> for ManualUnwrapOr
{
46 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
47 if in_external_macro(cx
.sess(), expr
.span
) || in_constant(cx
, expr
.hir_id
) {
50 lint_manual_unwrap_or(cx
, expr
);
54 fn lint_manual_unwrap_or
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
55 fn applicable_or_arm
<'a
>(cx
: &LateContext
<'_
>, arms
: &'a
[Arm
<'a
>]) -> Option
<&'a Arm
<'a
>> {
58 if arms
.iter().all(|arm
| arm
.guard
.is_none());
59 if let Some((idx
, or_arm
)) = arms
.iter().enumerate().find(|(_
, arm
)| {
61 PatKind
::Path(ref qpath
) => is_lang_ctor(cx
, qpath
, OptionNone
),
62 PatKind
::TupleStruct(ref qpath
, [pat
], _
) =>
63 matches
!(pat
.kind
, PatKind
::Wild
) && is_lang_ctor(cx
, qpath
, ResultErr
),
67 let unwrap_arm
= &arms
[1 - idx
];
68 if let PatKind
::TupleStruct(ref qpath
, [unwrap_pat
], _
) = unwrap_arm
.pat
.kind
;
69 if is_lang_ctor(cx
, qpath
, OptionSome
) || is_lang_ctor(cx
, qpath
, ResultOk
);
70 if let PatKind
::Binding(_
, binding_hir_id
, ..) = unwrap_pat
.kind
;
71 if path_to_local_id(unwrap_arm
.body
, binding_hir_id
);
72 if cx
.typeck_results().expr_adjustments(unwrap_arm
.body
).is_empty();
73 if !contains_return_break_continue_macro(or_arm
.body
);
83 if let ExprKind
::Match(scrutinee
, match_arms
, _
) = expr
.kind
;
84 let ty
= cx
.typeck_results().expr_ty(scrutinee
);
85 if let Some(ty_name
) = if is_type_diagnostic_item(cx
, ty
, sym
::option_type
) {
87 } else if is_type_diagnostic_item(cx
, ty
, sym
::result_type
) {
92 if let Some(or_arm
) = applicable_or_arm(cx
, match_arms
);
93 if let Some(or_body_snippet
) = snippet_opt(cx
, or_arm
.body
.span
);
94 if let Some(indent
) = indent_of(cx
, expr
.span
);
95 if constant_simple(cx
, cx
.typeck_results(), or_arm
.body
).is_some();
97 let reindented_or_body
=
98 reindent_multiline(or_body_snippet
.into(), true, Some(indent
));
100 let suggestion
= if scrutinee
.span
.from_expansion() {
101 // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
102 sugg
::Sugg
::hir_with_macro_callsite(cx
, scrutinee
, "..")
105 sugg
::Sugg
::hir(cx
, scrutinee
, "..").maybe_par()
110 MANUAL_UNWRAP_OR
, expr
.span
,
111 &format
!("this pattern reimplements `{}::unwrap_or`", ty_name
),
118 Applicability
::MachineApplicable
,