1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet_with_applicability;
3 use clippy_utils::{higher, is_wild};
4 use rustc_ast::{Attribute, LitKind};
5 use rustc_errors::Applicability;
6 use rustc_hir::{BorrowKind, Expr, ExprKind, Guard, MatchSource, Pat};
7 use rustc_lint::LateContext;
9 use rustc_span::source_map::Spanned;
11 use super::MATCH_LIKE_MATCHES_MACRO;
13 /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
14 pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
15 if let Some(higher::IfLet {
19 if_else: Some(if_else),
20 }) = higher::IfLet::hir(cx, expr)
22 return find_matches_sugg(
25 IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
31 if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
32 return find_matches_sugg(
35 arms.iter().map(|arm| {
37 cx.tcx.hir().attrs(arm.hir_id),
51 /// Lint a `match` or `if let` for replacement by `matches!`
52 fn find_matches_sugg<'a, 'b, I>(
69 Option<&'a Guard<'b>>,
75 if cx.typeck_results().expr_ty(expr).is_bool();
76 if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
77 let iter_without_last = iter.clone();
78 if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
79 if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
80 if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
82 if first_guard.is_none() || iter.len() == 0;
83 if first_attrs.is_empty();
86 find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
89 if let Some(last_pat) = last_pat_opt {
90 if !is_wild(last_pat) {
95 // The suggestion may be incorrect, because some arms can have `cfg` attributes
96 // evaluated into `false` and so such arms will be stripped before.
97 let mut applicability = Applicability::MaybeIncorrect;
99 use itertools::Itertools as _;
102 let pat_span = arm.1?.span;
103 Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
107 let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
108 format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
113 // strip potential borrows (#6503), but only if the type is a reference
115 if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
116 if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
122 MATCH_LIKE_MATCHES_MACRO,
124 &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
127 "{}matches!({}, {})",
128 if b0 { "" } else { "!" },
129 snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
141 /// Extract a `bool` or `{ bool }`
142 fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
144 ExprKind::Lit(Spanned {
145 node: LitKind::Bool(b), ..
155 if let ExprKind::Lit(Spanned {
156 node: LitKind::Bool(b), ..