1 use super::NEEDLESS_MATCH
;
2 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
3 use clippy_utils
::source
::snippet_with_applicability
;
4 use clippy_utils
::ty
::is_type_diagnostic_item
;
5 use clippy_utils
::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt}
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::LangItem
::OptionNone
;
8 use rustc_hir
::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp}
;
9 use rustc_lint
::LateContext
;
12 pub(crate) fn check_match(cx
: &LateContext
<'_
>, ex
: &Expr
<'_
>, arms
: &[Arm
<'_
>]) {
13 // This is for avoiding collision with `match_single_binding`.
19 if let PatKind
::Wild
= arm
.pat
.kind
{
20 let ret_expr
= strip_return(arm
.body
);
21 if !eq_expr_value(cx
, ex
, ret_expr
) {
24 } else if !pat_same_as_expr(arm
.pat
, arm
.body
) {
29 if let Some(match_expr
) = get_parent_expr(cx
, ex
) {
30 let mut applicability
= Applicability
::MachineApplicable
;
35 "this match expression is unnecessary",
37 snippet_with_applicability(cx
, ex
.span
, "..", &mut applicability
).to_string(),
43 /// Check for nop `if let` expression that assembled as unnecessary match
46 /// if let Some(a) = option {
54 /// if let SomeEnum::A = some_enum {
56 /// } else if let SomeEnum::B = some_enum {
62 pub(crate) fn check(cx
: &LateContext
<'_
>, ex
: &Expr
<'_
>) {
64 if let Some(ref if_let
) = higher
::IfLet
::hir(cx
, ex
);
65 if !is_else_clause(cx
.tcx
, ex
);
66 if check_if_let(cx
, if_let
);
68 let mut applicability
= Applicability
::MachineApplicable
;
73 "this if-let expression is unnecessary",
75 snippet_with_applicability(cx
, if_let
.let_expr
.span
, "..", &mut applicability
).to_string(),
82 fn check_if_let(cx
: &LateContext
<'_
>, if_let
: &higher
::IfLet
<'_
>) -> bool
{
83 if let Some(if_else
) = if_let
.if_else
{
84 if !pat_same_as_expr(if_let
.let_pat
, peel_blocks_with_stmt(if_let
.if_then
)) {
88 // Recurrsively check for each `else if let` phrase,
89 if let Some(ref nested_if_let
) = higher
::IfLet
::hir(cx
, if_else
) {
90 return check_if_let(cx
, nested_if_let
);
93 if matches
!(if_else
.kind
, ExprKind
::Block(..)) {
94 let else_expr
= peel_blocks_with_stmt(if_else
);
95 let ret
= strip_return(else_expr
);
96 let let_expr_ty
= cx
.typeck_results().expr_ty(if_let
.let_expr
);
97 if is_type_diagnostic_item(cx
, let_expr_ty
, sym
::Option
) {
98 if let ExprKind
::Path(ref qpath
) = ret
.kind
{
99 return is_lang_ctor(cx
, qpath
, OptionNone
) || eq_expr_value(cx
, if_let
.let_expr
, ret
);
102 return eq_expr_value(cx
, if_let
.let_expr
, ret
);
110 /// Strip `return` keyword if the expression type is `ExprKind::Ret`.
111 fn strip_return
<'hir
>(expr
: &'hir Expr
<'hir
>) -> &'hir Expr
<'hir
> {
112 if let ExprKind
::Ret(Some(ret
)) = expr
.kind
{
119 fn pat_same_as_expr(pat
: &Pat
<'_
>, expr
: &Expr
<'_
>) -> bool
{
120 let expr
= strip_return(expr
);
121 match (&pat
.kind
, &expr
.kind
) {
122 // Example: `Some(val) => Some(val)`
124 PatKind
::TupleStruct(QPath
::Resolved(_
, path
), [first_pat
, ..], _
),
125 ExprKind
::Call(call_expr
, [first_param
, ..]),
127 if let ExprKind
::Path(QPath
::Resolved(_
, call_path
)) = call_expr
.kind
{
128 if has_identical_segments(path
.segments
, call_path
.segments
)
129 && has_same_non_ref_symbol(first_pat
, first_param
)
135 // Example: `val => val`, or `ref val => *val`
136 (PatKind
::Binding(annot
, _
, pat_ident
, _
), _
) => {
137 let new_expr
= if let (
138 BindingAnnotation
::Ref
| BindingAnnotation
::RefMut
,
139 ExprKind
::Unary(UnOp
::Deref
, operand_expr
),
140 ) = (annot
, &expr
.kind
)
147 if let ExprKind
::Path(QPath
::Resolved(
150 segments
: [first_seg
, ..],
155 return pat_ident
.name
== first_seg
.ident
.name
;
158 // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
159 (PatKind
::Path(QPath
::Resolved(_
, p_path
)), ExprKind
::Path(QPath
::Resolved(_
, e_path
))) => {
160 return has_identical_segments(p_path
.segments
, e_path
.segments
);
163 (PatKind
::Lit(pat_lit_expr
), ExprKind
::Lit(expr_spanned
)) => {
164 if let ExprKind
::Lit(pat_spanned
) = &pat_lit_expr
.kind
{
165 return pat_spanned
.node
== expr_spanned
.node
;
174 fn has_identical_segments(left_segs
: &[PathSegment
<'_
>], right_segs
: &[PathSegment
<'_
>]) -> bool
{
175 if left_segs
.len() != right_segs
.len() {
178 for i
in 0..left_segs
.len() {
179 if left_segs
[i
].ident
.name
!= right_segs
[i
].ident
.name
{
186 fn has_same_non_ref_symbol(pat
: &Pat
<'_
>, expr
: &Expr
<'_
>) -> bool
{
188 if let PatKind
::Binding(annot
, _
, pat_ident
, _
) = pat
.kind
;
189 if !matches
!(annot
, BindingAnnotation
::Ref
| BindingAnnotation
::RefMut
);
190 if let ExprKind
::Path(QPath
::Resolved(_
, Path {segments: [first_seg, ..], .. }
)) = expr
.kind
;
192 return pat_ident
.name
== first_seg
.ident
.name
;