1 use crate::consts
::constant_simple
;
3 use crate::utils
::{path_to_local_id, sugg}
;
4 use if_chain
::if_chain
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::{Arm, Expr, ExprKind, Pat, PatKind}
;
7 use rustc_lint
::LintContext
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_middle
::lint
::in_external_macro
;
10 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
13 declare_clippy_lint
! {
15 /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
17 /// **Why is this bad?**
18 /// Concise code helps focusing on behavior instead of boilerplate.
20 /// **Known problems:** None.
24 /// let foo: Option<i32> = None;
33 /// let foo: Option<i32> = None;
38 "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
41 declare_lint_pass
!(ManualUnwrapOr
=> [MANUAL_UNWRAP_OR
]);
43 impl LateLintPass
<'_
> for ManualUnwrapOr
{
44 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
45 if in_external_macro(cx
.sess(), expr
.span
) {
48 lint_manual_unwrap_or(cx
, expr
);
52 #[derive(Copy, Clone)]
59 fn unwrap_fn_path(&self) -> &str {
61 Case
::Option
=> "Option::unwrap_or",
62 Case
::Result
=> "Result::unwrap_or",
67 fn lint_manual_unwrap_or
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
68 fn applicable_or_arm
<'a
>(arms
: &'a
[Arm
<'a
>]) -> Option
<&'a Arm
<'a
>> {
71 if arms
.iter().all(|arm
| arm
.guard
.is_none());
72 if let Some((idx
, or_arm
)) = arms
.iter().enumerate().find(|(_
, arm
)|
74 PatKind
::Path(ref some_qpath
) =>
75 utils
::match_qpath(some_qpath
, &utils
::paths
::OPTION_NONE
),
76 PatKind
::TupleStruct(ref err_qpath
, &[Pat { kind: PatKind::Wild, .. }
], _
) =>
77 utils
::match_qpath(err_qpath
, &utils
::paths
::RESULT_ERR
),
81 let unwrap_arm
= &arms
[1 - idx
];
82 if let PatKind
::TupleStruct(ref unwrap_qpath
, &[unwrap_pat
], _
) = unwrap_arm
.pat
.kind
;
83 if utils
::match_qpath(unwrap_qpath
, &utils
::paths
::OPTION_SOME
)
84 || utils
::match_qpath(unwrap_qpath
, &utils
::paths
::RESULT_OK
);
85 if let PatKind
::Binding(_
, binding_hir_id
, ..) = unwrap_pat
.kind
;
86 if path_to_local_id(unwrap_arm
.body
, binding_hir_id
);
87 if !utils
::usage
::contains_return_break_continue_macro(or_arm
.body
);
97 if let ExprKind
::Match(scrutinee
, match_arms
, _
) = expr
.kind
;
98 let ty
= cx
.typeck_results().expr_ty(scrutinee
);
99 if let Some(case
) = if utils
::is_type_diagnostic_item(cx
, ty
, sym
::option_type
) {
101 } else if utils
::is_type_diagnostic_item(cx
, ty
, sym
::result_type
) {
106 if let Some(or_arm
) = applicable_or_arm(match_arms
);
107 if let Some(or_body_snippet
) = utils
::snippet_opt(cx
, or_arm
.body
.span
);
108 if let Some(indent
) = utils
::indent_of(cx
, expr
.span
);
109 if constant_simple(cx
, cx
.typeck_results(), or_arm
.body
).is_some();
111 let reindented_or_body
=
112 utils
::reindent_multiline(or_body_snippet
.into(), true, Some(indent
));
113 utils
::span_lint_and_sugg(
115 MANUAL_UNWRAP_OR
, expr
.span
,
116 &format
!("this pattern reimplements `{}`", case
.unwrap_fn_path()),
120 sugg
::Sugg
::hir(cx
, scrutinee
, "..").maybe_par(),
123 Applicability
::MachineApplicable
,