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.
24 /// **Known problems:** None.
28 /// let foo: Option<i32> = None;
37 /// let foo: Option<i32> = None;
42 "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
45 declare_lint_pass
!(ManualUnwrapOr
=> [MANUAL_UNWRAP_OR
]);
47 impl LateLintPass
<'_
> for ManualUnwrapOr
{
48 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
49 if in_external_macro(cx
.sess(), expr
.span
) || in_constant(cx
, expr
.hir_id
) {
52 lint_manual_unwrap_or(cx
, expr
);
56 fn lint_manual_unwrap_or
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
57 fn applicable_or_arm
<'a
>(cx
: &LateContext
<'_
>, arms
: &'a
[Arm
<'a
>]) -> Option
<&'a Arm
<'a
>> {
60 if arms
.iter().all(|arm
| arm
.guard
.is_none());
61 if let Some((idx
, or_arm
)) = arms
.iter().enumerate().find(|(_
, arm
)| {
63 PatKind
::Path(ref qpath
) => is_lang_ctor(cx
, qpath
, OptionNone
),
64 PatKind
::TupleStruct(ref qpath
, &[pat
], _
) =>
65 matches
!(pat
.kind
, PatKind
::Wild
) && is_lang_ctor(cx
, qpath
, ResultErr
),
69 let unwrap_arm
= &arms
[1 - idx
];
70 if let PatKind
::TupleStruct(ref qpath
, &[unwrap_pat
], _
) = unwrap_arm
.pat
.kind
;
71 if is_lang_ctor(cx
, qpath
, OptionSome
) || is_lang_ctor(cx
, qpath
, ResultOk
);
72 if let PatKind
::Binding(_
, binding_hir_id
, ..) = unwrap_pat
.kind
;
73 if path_to_local_id(unwrap_arm
.body
, binding_hir_id
);
74 if cx
.typeck_results().expr_adjustments(unwrap_arm
.body
).is_empty();
75 if !contains_return_break_continue_macro(or_arm
.body
);
85 if let ExprKind
::Match(scrutinee
, match_arms
, _
) = expr
.kind
;
86 let ty
= cx
.typeck_results().expr_ty(scrutinee
);
87 if let Some(ty_name
) = if is_type_diagnostic_item(cx
, ty
, sym
::option_type
) {
89 } else if is_type_diagnostic_item(cx
, ty
, sym
::result_type
) {
94 if let Some(or_arm
) = applicable_or_arm(cx
, match_arms
);
95 if let Some(or_body_snippet
) = snippet_opt(cx
, or_arm
.body
.span
);
96 if let Some(indent
) = indent_of(cx
, expr
.span
);
97 if constant_simple(cx
, cx
.typeck_results(), or_arm
.body
).is_some();
99 let reindented_or_body
=
100 reindent_multiline(or_body_snippet
.into(), true, Some(indent
));
102 let suggestion
= if scrutinee
.span
.from_expansion() {
103 // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
104 sugg
::Sugg
::hir_with_macro_callsite(cx
, scrutinee
, "..")
107 sugg
::Sugg
::hir(cx
, scrutinee
, "..").maybe_par()
112 MANUAL_UNWRAP_OR
, expr
.span
,
113 &format
!("this pattern reimplements `{}::unwrap_or`", ty_name
),
120 Applicability
::MachineApplicable
,