use crate::ast::{AstNode, AstToken, BinaryExpr, Condition, Expr};
use crate::linter::diagnostic::Diagnostic;
use crate::linter::rules::{Example, Rule, RuleContext};
use crate::syntax::{SyntaxElement, SyntaxKind};
pub struct ConstantCondition;
impl Rule for ConstantCondition {
fn id(&self) -> &'static str {
"constant-condition"
}
fn description(&self) -> &'static str {
"Flag a `true`/`false` literal used as an `if`/`elseif`/`while` test or \
as an operand of the short-circuit `&&`/`||`. The branch or \
short-circuit is decided before the code runs, so the literal is \
usually leftover debugging or a mistyped name. `while true` is exempt \
as Julia's idiomatic infinite loop. No fix: removing the constant \
means restructuring the branch."
}
fn examples(&self) -> &'static [Example] {
&[
Example {
caption: "A literal `if` test always takes the branch:",
source: "if true\n println(\"always\")\nend\n",
},
Example {
caption: "A literal operand decides `&&` at parse time:",
source: "ok = false && check(x)\n",
},
]
}
fn interests(&self) -> &'static [SyntaxKind] {
&[SyntaxKind::CONDITION, SyntaxKind::BINARY_EXPR]
}
fn check(&self, el: &SyntaxElement, _ctx: &RuleContext<'_>, sink: &mut Vec<Diagnostic>) {
let Some(node) = el.as_node() else { return };
if let Some(cond) = Condition::cast(node.clone()) {
let Some(Expr::Literal(lit)) = cond.expr() else {
return;
};
let Some(tok) = lit.bool_token() else { return };
if tok.kind() == SyntaxKind::TRUE_KW
&& cond
.syntax()
.parent()
.is_some_and(|p| p.kind() == SyntaxKind::WHILE_EXPR)
{
return;
}
sink.push(Diagnostic::new(
self.id(),
lit.syntax().text_range(),
format!("this condition is always `{}`", tok.text()),
));
} else if let Some(bin) = BinaryExpr::cast(node.clone()) {
let Some(op) = bin
.op()
.filter(|op| matches!(op.syntax().kind(), SyntaxKind::AND_AND | SyntaxKind::OR_OR))
else {
return;
};
for operand in [bin.lhs(), bin.rhs()] {
let Some(Expr::Literal(lit)) = operand else {
continue;
};
let Some(tok) = lit.bool_token() else {
continue;
};
sink.push(Diagnostic::new(
self.id(),
lit.syntax().text_range(),
format!("`{}` has a constant `{}` operand", op.text(), tok.text()),
));
}
}
}
}