use diagnostics::LisetteDiagnostic;
use syntax::ast::{BinaryOperator, Expression, MatchOrigin, Span};
use crate::checker::infer::expressions::patterns::collect_pattern_bindings;
use super::helpers::{expressions_equivalent, is_empty_block};
pub fn check_identical_match_arms(
expression: &Expression,
diagnostics: &mut Vec<LisetteDiagnostic>,
) {
let Expression::Match {
subject,
arms,
origin,
span,
..
} = expression
else {
return;
};
if !matches!(origin, MatchOrigin::Explicit) {
return;
}
if arms.len() < 2 {
return;
}
if !is_safe_to_drop(subject) {
return;
}
if arms
.iter()
.any(|arm| arm.has_guard() || !collect_pattern_bindings(&arm.pattern).is_empty())
{
return;
}
let first = &arms[0].expression;
if is_empty_block(first) {
return;
}
if !arms[1..]
.iter()
.all(|arm| expressions_equivalent(first, &arm.expression))
{
return;
}
let match_keyword_span = Span::new(span.file_id, span.byte_offset, 5);
diagnostics.push(diagnostics::lint::identical_match_arms(&match_keyword_span));
}
fn is_safe_to_drop(expression: &Expression) -> bool {
let expression = expression.unwrap_parens();
let node_is_safe = match expression {
Expression::Identifier { .. }
| Expression::Literal { .. }
| Expression::Unary { .. }
| Expression::DotAccess { .. } => true,
Expression::Binary { operator, .. } => !matches!(
operator,
BinaryOperator::Division
| BinaryOperator::Remainder
| BinaryOperator::ShiftLeft
| BinaryOperator::ShiftRight
),
_ => false,
};
node_is_safe && expression.children().into_iter().all(is_safe_to_drop)
}