]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / matches / match_like_matches.rs
CommitLineData
5099ac24 1use clippy_utils::diagnostics::span_lint_and_sugg;
923072b8 2use clippy_utils::is_wild;
5099ac24 3use clippy_utils::source::snippet_with_applicability;
f2b60f7d 4use clippy_utils::span_contains_comment;
5099ac24
FG
5use rustc_ast::{Attribute, LitKind};
6use rustc_errors::Applicability;
5e7ed085 7use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
f2b60f7d 8use rustc_lint::{LateContext, LintContext};
5099ac24
FG
9use rustc_middle::ty;
10use rustc_span::source_map::Spanned;
11
12use super::MATCH_LIKE_MATCHES_MACRO;
13
14/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
923072b8
FG
15pub(crate) fn check_if_let<'tcx>(
16 cx: &LateContext<'tcx>,
17 expr: &'tcx Expr<'_>,
18 let_pat: &'tcx Pat<'_>,
19 let_expr: &'tcx Expr<'_>,
20 then_expr: &'tcx Expr<'_>,
21 else_expr: &'tcx Expr<'_>,
22) {
23 find_matches_sugg(
24 cx,
5099ac24 25 let_expr,
923072b8
FG
26 IntoIterator::into_iter([
27 (&[][..], Some(let_pat), then_expr, None),
28 (&[][..], None, else_expr, None),
29 ]),
30 expr,
31 true,
32 );
5e7ed085 33}
5099ac24 34
5e7ed085
FG
35pub(super) fn check_match<'tcx>(
36 cx: &LateContext<'tcx>,
37 e: &'tcx Expr<'_>,
38 scrutinee: &'tcx Expr<'_>,
39 arms: &'tcx [Arm<'tcx>],
40) -> bool {
41 find_matches_sugg(
42 cx,
43 scrutinee,
44 arms.iter().map(|arm| {
45 (
46 cx.tcx.hir().attrs(arm.hir_id),
47 Some(arm.pat),
48 arm.body,
49 arm.guard.as_ref(),
50 )
51 }),
52 e,
53 false,
54 )
5099ac24
FG
55}
56
57/// Lint a `match` or `if let` for replacement by `matches!`
58fn find_matches_sugg<'a, 'b, I>(
59 cx: &LateContext<'_>,
60 ex: &Expr<'_>,
61 mut iter: I,
62 expr: &Expr<'_>,
63 is_if_let: bool,
64) -> bool
65where
66 'b: 'a,
67 I: Clone
68 + DoubleEndedIterator
69 + ExactSizeIterator
70 + Iterator<
71 Item = (
72 &'a [Attribute],
73 Option<&'a Pat<'b>>,
74 &'a Expr<'b>,
75 Option<&'a Guard<'b>>,
76 ),
77 >,
78{
79 if_chain! {
f2b60f7d 80 if !span_contains_comment(cx.sess().source_map(), expr.span);
5099ac24
FG
81 if iter.len() >= 2;
82 if cx.typeck_results().expr_ty(expr).is_bool();
83 if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
84 let iter_without_last = iter.clone();
85 if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
064997fb
FG
86 if let Some(b0) = find_bool_lit(&first_expr.kind);
87 if let Some(b1) = find_bool_lit(&last_expr.kind);
5099ac24
FG
88 if b0 != b1;
89 if first_guard.is_none() || iter.len() == 0;
90 if first_attrs.is_empty();
91 if iter
92 .all(|arm| {
064997fb 93 find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
5099ac24
FG
94 });
95 then {
96 if let Some(last_pat) = last_pat_opt {
97 if !is_wild(last_pat) {
98 return false;
99 }
100 }
101
102 // The suggestion may be incorrect, because some arms can have `cfg` attributes
103 // evaluated into `false` and so such arms will be stripped before.
104 let mut applicability = Applicability::MaybeIncorrect;
105 let pat = {
106 use itertools::Itertools as _;
107 iter_without_last
108 .filter_map(|arm| {
109 let pat_span = arm.1?.span;
110 Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
111 })
112 .join(" | ")
113 };
114 let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
115 format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
116 } else {
117 pat
118 };
119
120 // strip potential borrows (#6503), but only if the type is a reference
121 let mut ex_new = ex;
122 if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
123 if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
124 ex_new = ex_inner;
125 }
126 };
127 span_lint_and_sugg(
128 cx,
129 MATCH_LIKE_MATCHES_MACRO,
130 expr.span,
131 &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
132 "try this",
133 format!(
134 "{}matches!({}, {})",
135 if b0 { "" } else { "!" },
136 snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
137 pat_and_guard,
138 ),
139 applicability,
140 );
141 true
142 } else {
143 false
144 }
145 }
146}
147
148/// Extract a `bool` or `{ bool }`
064997fb 149fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
5099ac24
FG
150 match ex {
151 ExprKind::Lit(Spanned {
152 node: LitKind::Bool(b), ..
153 }) => Some(*b),
154 ExprKind::Block(
155 rustc_hir::Block {
156 stmts: &[],
157 expr: Some(exp),
158 ..
159 },
160 _,
064997fb 161 ) => {
5099ac24
FG
162 if let ExprKind::Lit(Spanned {
163 node: LitKind::Bool(b), ..
164 }) = exp.kind
165 {
166 Some(b)
167 } else {
168 None
169 }
170 },
171 _ => None,
172 }
173}