1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::is_wild;
3 use clippy_utils::source::snippet_with_applicability;
4 use clippy_utils::span_contains_comment;
5 use rustc_ast::{Attribute, LitKind};
6 use rustc_errors::Applicability;
7 use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
8 use rustc_lint::{LateContext, LintContext};
10 use rustc_span::source_map::Spanned;
12 use super::MATCH_LIKE_MATCHES_MACRO;
14 /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
15 pub(crate) fn check_if_let<'tcx>(
16 cx: &LateContext<'tcx>,
18 let_pat: &'tcx Pat<'_>,
19 let_expr: &'tcx Expr<'_>,
20 then_expr: &'tcx Expr<'_>,
21 else_expr: &'tcx Expr<'_>,
26 IntoIterator::into_iter([
27 (&[][..], Some(let_pat), then_expr, None),
28 (&[][..], None, else_expr, None),
35 pub(super) fn check_match<'tcx>(
36 cx: &LateContext<'tcx>,
38 scrutinee: &'tcx Expr<'_>,
39 arms: &'tcx [Arm<'tcx>],
44 arms.iter().map(|arm| {
46 cx.tcx.hir().attrs(arm.hir_id),
57 /// Lint a `match` or `if let` for replacement by `matches!`
58 fn find_matches_sugg<'a, 'b, I>(
75 Option<&'a Guard<'b>>,
80 if !span_contains_comment(cx.sess().source_map(), expr.span);
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();
86 if let Some(b0) = find_bool_lit(&first_expr.kind);
87 if let Some(b1) = find_bool_lit(&last_expr.kind);
89 if first_guard.is_none() || iter.len() == 0;
90 if first_attrs.is_empty();
93 find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
96 if let Some(last_pat) = last_pat_opt {
97 if !is_wild(last_pat) {
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;
106 use itertools::Itertools as _;
109 let pat_span = arm.1?.span;
110 Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
114 let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
115 format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability))
120 // strip potential borrows (#6503), but only if the type is a reference
122 if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
123 if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
129 MATCH_LIKE_MATCHES_MACRO,
131 &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
134 "{}matches!({}, {pat_and_guard})",
135 if b0 { "" } else { "!" },
136 snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
147 /// Extract a `bool` or `{ bool }`
148 fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
150 ExprKind::Lit(Spanned {
151 node: LitKind::Bool(b), ..
161 if let ExprKind::Lit(Spanned {
162 node: LitKind::Bool(b), ..