use rowan::ast::AstNode as _;
use crate::linter::diagnostic::{Diagnostic, Fix, Severity, ViolationData};
use crate::linter::rules::matchers;
use crate::linter::rules::{Rule, RuleContext};
use crate::syntax::{SyntaxElement, SyntaxKind};
pub struct RedundantIfelse;
impl Rule for RedundantIfelse {
fn id(&self) -> &'static str {
"redundant-ifelse"
}
fn interests(&self) -> &'static [SyntaxKind] {
&[SyntaxKind::CALL_EXPR]
}
fn check(&self, el: &SyntaxElement, _ctx: &RuleContext<'_>, sink: &mut Vec<Diagnostic>) {
let Some(node) = el.as_node() else {
return;
};
let Some(call) = matchers::call_named(node, "ifelse") else {
return;
};
let all = matchers::args(&call);
if all.len() != 3 || all.iter().any(|a| a.name.is_some()) {
return;
}
let (Some(test), Some(yes), Some(no)) = (
all[0].value.as_ref(),
all[1].value.as_ref(),
all[2].value.as_ref(),
) else {
return;
};
let negate = match (
matchers::is_true(yes) && matchers::is_false(no),
matchers::is_false(yes) && matchers::is_true(no),
) {
(true, _) => false, (_, true) => true, _ => return,
};
let r = call.syntax().text_range();
let (start, end) = (usize::from(r.start()), usize::from(r.end()));
let fix = match negate {
false => Some(Fix::safe(
start,
end,
matchers::element_text(test),
"Replace `ifelse()` with the condition",
)),
true if matchers::is_atom(test) => Some(Fix::safe(
start,
end,
format!("!{}", matchers::element_text(test)),
"Replace `ifelse()` with the negated condition",
)),
true => None,
};
sink.push(Diagnostic {
rule: "redundant-ifelse",
severity: Severity::Warning,
path: Default::default(),
range: r,
message: ViolationData::new(
"redundant-ifelse",
"`ifelse()` returning `TRUE`/`FALSE` is redundant",
)
.with_suggestion("Use the condition directly, or negate it."),
fix,
});
}
}