1 use syntax::ast::{self, AstNode};
3 use crate::{AssistContext, AssistId, AssistKind, Assists};
5 // Assist: convert_two_arm_bool_match_to_matches_macro
7 // Convert 2-arm match that evaluates to a boolean into the equivalent matches! invocation.
11 // match scrutinee$0 {
12 // Some(val) if val.cond() => true,
20 // matches!(scrutinee, Some(val) if val.cond())
23 pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
25 ctx: &AssistContext<'_>,
27 let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
28 let match_arm_list = match_expr.match_arm_list()?;
29 let mut arms = match_arm_list.arms();
30 let first_arm = arms.next()?;
31 let second_arm = arms.next()?;
32 if arms.next().is_some() {
33 cov_mark::hit!(non_two_arm_match);
36 let first_arm_expr = first_arm.expr();
37 let second_arm_expr = second_arm.expr();
39 let invert_matches = if is_bool_literal_expr(&first_arm_expr, true)
40 && is_bool_literal_expr(&second_arm_expr, false)
43 } else if is_bool_literal_expr(&first_arm_expr, false)
44 && is_bool_literal_expr(&second_arm_expr, true)
48 cov_mark::hit!(non_invert_bool_literal_arms);
52 let target_range = ctx.sema.original_range(match_expr.syntax()).range;
53 let expr = match_expr.expr()?;
56 AssistId("convert_two_arm_bool_match_to_matches_macro", AssistKind::RefactorRewrite),
57 "Convert to matches!",
60 let mut arm_str = String::new();
61 if let Some(pat) = &first_arm.pat() {
62 arm_str += &pat.to_string();
64 if let Some(guard) = &first_arm.guard() {
65 arm_str += &format!(" {guard}");
68 builder.replace(target_range, format!("!matches!({expr}, {arm_str})"));
70 builder.replace(target_range, format!("matches!({expr}, {arm_str})"));
76 fn is_bool_literal_expr(expr: &Option<ast::Expr>, expect_bool: bool) -> bool {
77 if let Some(ast::Expr::Literal(lit)) = expr {
78 if let ast::LiteralKind::Bool(b) = lit.kind() {
79 return b == expect_bool;
88 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
90 use super::convert_two_arm_bool_match_to_matches_macro;
93 fn not_applicable_outside_of_range_left() {
94 check_assist_not_applicable(
95 convert_two_arm_bool_match_to_matches_macro,
97 fn foo(a: Option<u32>) -> bool {
108 fn not_applicable_non_two_arm_match() {
109 cov_mark::check!(non_two_arm_match);
110 check_assist_not_applicable(
111 convert_two_arm_bool_match_to_matches_macro,
113 fn foo(a: Option<u32>) -> bool {
125 fn not_applicable_non_bool_literal_arms() {
126 cov_mark::check!(non_invert_bool_literal_arms);
127 check_assist_not_applicable(
128 convert_two_arm_bool_match_to_matches_macro,
130 fn foo(a: Option<u32>) -> bool {
132 Some(val) => val == 3,
140 fn not_applicable_both_false_arms() {
141 cov_mark::check!(non_invert_bool_literal_arms);
142 check_assist_not_applicable(
143 convert_two_arm_bool_match_to_matches_macro,
145 fn foo(a: Option<u32>) -> bool {
156 fn not_applicable_both_true_arms() {
157 cov_mark::check!(non_invert_bool_literal_arms);
158 check_assist_not_applicable(
159 convert_two_arm_bool_match_to_matches_macro,
161 fn foo(a: Option<u32>) -> bool {
172 fn convert_simple_case() {
174 convert_two_arm_bool_match_to_matches_macro,
176 fn foo(a: Option<u32>) -> bool {
184 fn foo(a: Option<u32>) -> bool {
185 matches!(a, Some(_val))
192 fn convert_simple_invert_case() {
194 convert_two_arm_bool_match_to_matches_macro,
196 fn foo(a: Option<u32>) -> bool {
204 fn foo(a: Option<u32>) -> bool {
205 !matches!(a, Some(_val))
212 fn convert_with_guard_case() {
214 convert_two_arm_bool_match_to_matches_macro,
216 fn foo(a: Option<u32>) -> bool {
218 Some(val) if val > 3 => true,
224 fn foo(a: Option<u32>) -> bool {
225 matches!(a, Some(val) if val > 3)
232 fn convert_enum_match_cases() {
234 convert_two_arm_bool_match_to_matches_macro,
238 fn foo(a: X) -> bool {
248 fn foo(a: X) -> bool {
256 fn convert_target_simple() {
258 convert_two_arm_bool_match_to_matches_macro,
260 fn foo(a: Option<u32>) -> bool {
275 fn convert_target_complex() {
277 convert_two_arm_bool_match_to_matches_macro,